From f7916a0161657e641717dca19f7c81af2d081ec4 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 1 Mar 2023 17:47:51 -0500 Subject: Added basic search functionality: : --- src/client/views/collections/CollectionMenu.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 40 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 17f02711d..c83f4e689 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -746,7 +746,7 @@ export class CollectionFreeFormViewChrome extends React.Component{ + const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); + this._bingMap.current.setView({ + center: new this.MicrosoftMaps.Location(location.latitude, location.longitude), + zoom: 15, + }); + } + + + bingViewOptions = { center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng }, mapTypeId: 'grayscale', @@ -617,12 +637,31 @@ export class MapBox extends ViewBoxAnnotatableComponent + +
{renderAnnotations(this.transparentFilter)}
{renderAnnotations(this.opaqueFilter)} {SnappingManager.GetIsDragging() ? null : renderAnnotations()} {this.annotationLayer} + + + {!MapBox.UseBing ? null : + this.bingSearchBarContents = newText} + placeholder="..." + size="medium" + text="Boston, MA" + />} + {!MapBox.UseBing ? null : + } + + + + {!MapBox.UseBing ? null : } +
@@ -647,6 +686,7 @@ export class MapBox extends ViewBoxAnnotatableComponent +
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( Date: Wed, 15 Mar 2023 17:17:38 -0400 Subject: yuh --- src/client/views/nodes/MapBox/MapBox.tsx | 63 ++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 3c83698d8..f4f9e949f 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -22,6 +22,7 @@ import { AnchorMenu } from '../../pdf/AnchorMenu'; import { Annotation } from '../../pdf/Annotation'; import { SidebarAnnos } from '../../SidebarAnnos'; import { FieldView, FieldViewProps } from '../FieldView'; +import { PinProps } from '../trails/PresBox'; import './MapBox.scss'; import { MapBoxInfoWindow } from './MapBoxInfoWindow'; @@ -527,8 +528,7 @@ export class MapBox extends ViewBoxAnnotatableComponent AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; - + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; /** * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker * @returns @@ -581,7 +581,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { res(r.results[0].location); @@ -593,7 +593,9 @@ export class MapBox extends ViewBoxAnnotatableComponent{ + + // clear all pins + this._bingMap.current.entities.clear(); + const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); + + // this.rootDoc.latitude =location.latitude; + // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info? + //DocComponents file is where rootDoc is + + // call a helper method that updates the this._bingMap.current.setView, + // replaces this method call below this._bingMap.current.setView({ center: new this.MicrosoftMaps.Location(location.latitude, location.longitude), - zoom: 15, + // zoom: , + }); + + //Create custom Pushpin + var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { + title: this.bingSearchBarContents, + subTitle: 'subtitle here', + text: '1' }); + + //Add the pushpin to the map + this._bingMap.current.entities.push(pin); + // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {}); + // this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc } + // /** + // * For Bing Maps + // * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list + // * @param position - the LatLng position where the marker is placed + // * @param map + // */ + // @action + // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => { + // const marker = new google.maps.Marker({ + // position: position, + // map: map, + // }); + // map.panTo(position); + // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); + // this.addDocument(mapMarker, this.annotationKey); + // }; + + + + bingViewOptions = { - center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng }, + center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude mapTypeId: 'grayscale', }; bingMapOptions = { @@ -650,9 +695,11 @@ export class MapBox extends ViewBoxAnnotatableComponent this.bingSearchBarContents = newText} placeholder="..." - size="medium" + size="medium" text="Boston, MA" + onKeyPress={e => console.log(e.key)} />} + {!MapBox.UseBing ? null : } @@ -686,8 +733,8 @@ export class MapBox extends ViewBoxAnnotatableComponent - + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( Date: Tue, 4 Apr 2023 10:16:50 -0400 Subject: Fixed make map error --- package-lock.json | 191 +++----- src/client/views/nodes/MapBox/MapBox.tsx | 287 +++++++---- src/client/views/nodes/MapBox/MapBox2.tsx | 777 ++++++++++++++++++++++++++++++ 3 files changed, 1041 insertions(+), 214 deletions(-) create mode 100644 src/client/views/nodes/MapBox/MapBox2.tsx (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 3b834a75e..41d46fd62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5337,16 +5337,6 @@ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "d3-array": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", @@ -6569,28 +6559,6 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "dev": true, - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", @@ -6602,7 +6570,6 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { - "d": "^1.0.1", "ext": "^1.1.2" } }, @@ -13650,7 +13617,7 @@ "dependencies": { "@iarna/cli": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@iarna/cli/-/cli-2.1.0.tgz", + "resolved": false, "integrity": "sha512-rvVVqDa2g860niRbqs3D5RhL4la3dc1vwk+NlpKPZxKaMSHtE2se6C2x8NeveN+rcjp3/686X+u+09CZ+7lmAQ==", "requires": { "glob": "^7.1.2", @@ -13753,7 +13720,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -13768,7 +13735,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -13777,12 +13744,12 @@ }, "asap": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "resolved": false, "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "asn1": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "resolved": false, "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "requires": { "safer-buffer": "~2.1.0" @@ -13790,7 +13757,7 @@ }, "assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "resolved": false, "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" }, "asynckit": { @@ -13800,22 +13767,22 @@ }, "aws-sign2": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "resolved": false, "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" }, "aws4": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "resolved": false, "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "resolved": false, "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "resolved": false, "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "requires": { "tweetnacl": "^0.14.3" @@ -13836,7 +13803,7 @@ }, "bluebird": { "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "resolved": false, "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "boxen": { @@ -13884,7 +13851,7 @@ }, "cacache": { "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "resolved": false, "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", "requires": { "bluebird": "^3.5.5", @@ -13921,7 +13888,7 @@ }, "caseless": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "resolved": false, "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, "chalk": { @@ -14065,7 +14032,7 @@ }, "combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "resolved": false, "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" @@ -14073,7 +14040,7 @@ }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "concat-stream": { @@ -14103,7 +14070,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14118,7 +14085,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14127,7 +14094,7 @@ }, "config-chain": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "resolved": false, "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "requires": { "ini": "^1.3.4", @@ -14228,7 +14195,7 @@ }, "dashdash": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "resolved": false, "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "requires": { "assert-plus": "^1.0.0" @@ -14261,7 +14228,7 @@ }, "decode-uri-component": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "resolved": false, "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "deep-extend": { @@ -14307,7 +14274,7 @@ }, "dezalgo": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "resolved": false, "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "requires": { "asap": "^2.0.0", @@ -14359,7 +14326,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14374,7 +14341,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14383,7 +14350,7 @@ }, "ecc-jsbn": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "resolved": false, "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "requires": { "jsbn": "~0.1.0", @@ -14418,7 +14385,7 @@ }, "env-paths": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "resolved": false, "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" }, "err-code": { @@ -14502,7 +14469,7 @@ }, "extsprintf": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "resolved": false, "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" }, "fast-json-stable-stringify": { @@ -14512,12 +14479,12 @@ }, "figgy-pudding": { "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "resolved": false, "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" }, "filter-obj": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "resolved": false, "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" }, "find-npm-prefix": { @@ -14550,7 +14517,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14565,7 +14532,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14574,12 +14541,12 @@ }, "forever-agent": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "resolved": false, "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" }, "form-data": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "resolved": false, "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "requires": { "asynckit": "^0.4.0", @@ -14612,7 +14579,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14627,7 +14594,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14695,7 +14662,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14710,7 +14677,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14719,7 +14686,7 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "function-bind": { @@ -14809,7 +14776,7 @@ }, "getpass": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "resolved": false, "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "requires": { "assert-plus": "^1.0.0" @@ -14817,7 +14784,7 @@ }, "glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "resolved": false, "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", @@ -14830,7 +14797,7 @@ "dependencies": { "minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "resolved": false, "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" @@ -14873,12 +14840,12 @@ }, "graceful-fs": { "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "resolved": false, "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "har-schema": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "resolved": false, "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" }, "har-validator": { @@ -14957,7 +14924,7 @@ }, "http-signature": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "resolved": false, "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "requires": { "assert-plus": "^1.0.0", @@ -15084,7 +15051,7 @@ }, "is-cidr": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.1.1.tgz", + "resolved": false, "integrity": "sha512-Gx+oErgq1j2jAKCR2Kbq0b3wbH0vQKqZ0wOlHxm0o56nq51Cs/DZA8oz9dMDhbHyHEGgJ86eTeVudtgMMOx3Mw==", "requires": { "cidr-regex": "^2.0.10" @@ -15163,7 +15130,7 @@ }, "is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "resolved": false, "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, "isarray": { @@ -15178,12 +15145,12 @@ }, "isstream": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "resolved": false, "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "jsbn": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "resolved": false, "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, "json-parse-better-errors": { @@ -15193,7 +15160,7 @@ }, "json-parse-even-better-errors": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "resolved": false, "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema": { @@ -15203,7 +15170,7 @@ }, "json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "resolved": false, "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "jsonparse": { @@ -15421,7 +15388,7 @@ }, "lock-verify": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.2.2.tgz", + "resolved": false, "integrity": "sha512-2CUNtr1ZSVKJHcYP8uEzafmmuyauCB5zZimj8TvQd/Lflt9kXVZs+8S+EbAzZLaVUDn8CYGmeC3DFGdYfnCzeQ==", "requires": { "@iarna/cli": "^2.1.0", @@ -15550,7 +15517,7 @@ }, "meant": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.3.tgz", + "resolved": false, "integrity": "sha512-88ZRGcNxAq4EH38cQ4D85PM57pikCwS8Z99EWHODxN7KBY+UuPiqzRTtZzS8KTXO/ywSWbdjjJST2Hly/EQxLw==" }, "mime-db": { @@ -15568,7 +15535,7 @@ }, "minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "resolved": false, "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" @@ -15617,7 +15584,7 @@ }, "mkdirp": { "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "resolved": false, "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "requires": { "minimist": "^1.2.6" @@ -15665,7 +15632,7 @@ }, "node-gyp": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz", + "resolved": false, "integrity": "sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw==", "requires": { "env-paths": "^2.2.0", @@ -16003,7 +15970,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16018,7 +15985,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16032,7 +15999,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-is-inside": { @@ -16052,7 +16019,7 @@ }, "performance-now": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "resolved": false, "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "pify": { @@ -16101,7 +16068,7 @@ }, "proto-list": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "resolved": false, "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" }, "protoduck": { @@ -16124,7 +16091,7 @@ }, "psl": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "resolved": false, "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "pump": { @@ -16164,12 +16131,12 @@ }, "qs": { "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "resolved": false, "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" }, "query-string": { "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "resolved": false, "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", "requires": { "decode-uri-component": "^0.2.0", @@ -16180,7 +16147,7 @@ }, "qw": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.2.tgz", + "resolved": false, "integrity": "sha512-1PhZ/iLKwlVNq45dnerTMKFjMof49uqli7/0QsvPNbX5OJ3IZ8msa9lUpvPheVdP+IYYPrf6cOaVil7S35joVA==" }, "rc": { @@ -16226,7 +16193,7 @@ }, "read-package-json": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "resolved": false, "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", "requires": { "glob": "^7.1.1", @@ -16285,7 +16252,7 @@ }, "request": { "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "resolved": false, "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "requires": { "aws-sign2": "~0.7.0", @@ -16355,7 +16322,7 @@ }, "safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "resolved": false, "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safer-buffer": { @@ -16526,7 +16493,7 @@ }, "sshpk": { "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "resolved": false, "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "requires": { "asn1": "~0.2.3", @@ -16582,7 +16549,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16597,7 +16564,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16611,7 +16578,7 @@ }, "strict-uri-encode": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "resolved": false, "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" }, "string-width": { @@ -16767,7 +16734,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16782,7 +16749,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16801,7 +16768,7 @@ }, "tough-cookie": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "resolved": false, "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { "psl": "^1.1.28", @@ -16810,7 +16777,7 @@ "dependencies": { "punycode": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "resolved": false, "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" } } @@ -16825,7 +16792,7 @@ }, "tweetnacl": { "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "resolved": false, "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "typedarray": { @@ -16896,7 +16863,7 @@ }, "uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "resolved": false, "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "requires": { "punycode": "^2.1.0" @@ -16937,7 +16904,7 @@ }, "uuid": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "resolved": false, "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "validate-npm-package-license": { @@ -16959,7 +16926,7 @@ }, "verror": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "resolved": false, "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "requires": { "assert-plus": "^1.0.0", @@ -22156,7 +22123,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-check": { "version": "0.4.0", diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index d35a5ef2e..b01426bcd 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -201,6 +201,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { @@ -219,6 +220,27 @@ export class MapBox extends ViewBoxAnnotatableComponent { + place[Id] ? (this.markerMap[place[Id]] = marker) : null; + }; + + /** + * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true + * @param e + * @param place + */ + @action + private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => { + // set which place was clicked + this.selectedPlace = place; + place.infoWindowOpen = true; + }; /** * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list @@ -299,27 +321,52 @@ export class MapBox extends ViewBoxAnnotatableComponent { - place[Id] ? (this.markerMap[place[Id]] = marker) : null; - }; + private handlePlaceChanged = () => { + const place = this.searchBox.getPlace(); - /** - * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true - * @param e - * @param place - */ - @action - private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => { - // set which place was clicked - this.selectedPlace = place; - place.infoWindowOpen = true; + if (!place.geometry || !place.geometry.location) { + // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed + window.alert("No details available for input: '" + place.name + "'"); + return; + } + + // zoom in on the location of the search result + if (place.geometry.viewport) { + this._map.fitBounds(place.geometry.viewport); + } else { + this._map.setCenter(place.geometry.location); + this._map.setZoom(17); + } + + // customize icon => customized icon for the nature of the location selected + const icon = { + url: place.icon as string, + size: new google.maps.Size(71, 71), + origin: new google.maps.Point(0, 0), + anchor: new google.maps.Point(17, 34), + scaledSize: new google.maps.Size(25, 25), + }; + + // put temporary cutomized marker on searched location + this.searchMarkers.forEach(marker => { + marker.setMap(null); + }); + this.searchMarkers = []; + this.searchMarkers.push( + new window.google.maps.Marker({ + map: this._map, + icon, + title: place.name, + position: place.geometry.location, + }) + ); }; + /** * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts * @param doc @@ -397,52 +444,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { - const place = this.searchBox.getPlace(); - - if (!place.geometry || !place.geometry.location) { - // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed - window.alert("No details available for input: '" + place.name + "'"); - return; - } - - // zoom in on the location of the search result - if (place.geometry.viewport) { - this._map.fitBounds(place.geometry.viewport); - } else { - this._map.setCenter(place.geometry.location); - this._map.setZoom(17); - } - - // customize icon => customized icon for the nature of the location selected - const icon = { - url: place.icon as string, - size: new google.maps.Size(71, 71), - origin: new google.maps.Point(0, 0), - anchor: new google.maps.Point(17, 34), - scaledSize: new google.maps.Size(25, 25), - }; - - // put temporary cutomized marker on searched location - this.searchMarkers.forEach(marker => { - marker.setMap(null); - }); - this.searchMarkers = []; - this.searchMarkers.push( - new window.google.maps.Marker({ - map: this._map, - icon, - title: place.name, - position: place.geometry.location, - }) - ); - }; - /** * Handles toggle of sidebar on click the little comment button */ @@ -595,71 +596,153 @@ export class MapBox extends ViewBoxAnnotatableComponent{ - - // clear all pins - this._bingMap.current.entities.clear(); - - const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); - - // this.rootDoc.latitude =location.latitude; - // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info? - //DocComponents file is where rootDoc is - - // call a helper method that updates the this._bingMap.current.setView, - // replaces this method call below - this._bingMap.current.setView({ - center: new this.MicrosoftMaps.Location(location.latitude, location.longitude), - // zoom: , - }); - - //Create custom Pushpin - var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { - title: this.bingSearchBarContents, - subTitle: 'subtitle here', - text: '1' - }); - - //Add the pushpin to the map - this._bingMap.current.entities.push(pin); - // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {}); - // this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc - } + bingSearch = async() =>{ //TODO: PlaceResult, searching more formally + + // clear all pins + // this._bingMap.current.entities.clear(); + + const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); + + this.rootDoc.latitude = location.latitude; + this.rootDoc.longitude = location.longitude; + + // this.rootDoc.latitude =location.latitude; + // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info? + //DocComponents file is where rootDoc is + + // call a helper method that updates the this._bingMap.current.setView, + // replaces this method call below + this._bingMap.current.setView({ + center: new this.MicrosoftMaps.Location(location.latitude, location.longitude), + // zoom: location + }); + // this.MicrosoftMaps.SpatialDataService.GeoDataAPIManager.getBoundary( + // this._bingMap.current.getCenter(), + // this.geoDataRequestOptions, + // this._bingMap.current, + // function (data) { + // if (data.results && data.results.length > 0) { + // map.entities.push(data.results[0].Polygons); + // } + // }, + // null, + // function errCallback(networkStatus, statusMessage) { + // console.log(networkStatus); + // console.log(statusMessage); + // } + // ); + + //Create custom Pushpin + var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { + title: this.bingSearchBarContents, + subTitle: 'subtitle here', + text: '1' + }); + + //Add the pushpin to the map + this._bingMap.current.entities.push(pin); + // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {}); + // this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc + } + + // _loadPending = true; + // /** + // * store a reference to google map instance + // * setup the drawing manager on the top right corner of map + // * fit map bounds to contain all markers + // * + // */ + // @action + // private loadHandler = () => { + + // // this._loadPending = true; + + // // // for making GoogleMap markers + // // // const centerControlDiv = this.CenterControl(); + // // // map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv); + + // // // this._bingMap.current. + + // // map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5)); + // // map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng))); + // // setTimeout(() => { + // // if (this._loadPending && this._map.getBounds()) { + // // this._loadPending = false; + // // this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + // // } + // // }, 250); + + // // // listener to addmarker event, creates pushpin onClick + // // this._bingMap.addListener('click', (e: MouseEvent) => { + // // if (this.toggleAddMarker === true) { + // // this.placeMarker((e as any).latLng, map); //TODO: Implement placeMarker + // // } + // // }); + // }; // /** // * For Bing Maps // * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list - // * @param position - the LatLng position where the marker is placed - // * @param map + // * @param location - this.MicrosoftMaps.Location + // * @param map - this._bingMap // */ // @action - // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => { - // const marker = new google.maps.Marker({ - // position: position, - // map: map, + // private placeMarker = (location: any) => { + // const pin = new this.MicrosoftMaps.Pushpin(location, { + // title: this.bingSearchBarContents, + // subTitle: 'subtitle here', + // text: '1' // }); - // map.panTo(position); - // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); - // this.addDocument(mapMarker, this.annotationKey); - // }; + // this._bingMap.current.panTo(location); + // this._bingMap.current.entities.push(pin); + // // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(location.latitude), NumCast(location.longitude), false, [], {}); + // // this.addDocument(mapMarker, this.annotationKey); + // }; + + /** + * View options for bing maps + */ bingViewOptions = { center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude mapTypeId: 'grayscale', }; + + /** + * Map options + * TODO: CHANGE TO BE MORE USER-FRIENDLY + */ bingMapOptions = { navigationBarMode: 'square', }; diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx new file mode 100644 index 000000000..c11f76439 --- /dev/null +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -0,0 +1,777 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api'; +import BingMapsReact from 'bingmaps-react'; +import { EditableText } from 'browndash-components'; +import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc'; +import { Id } from '../../../../fields/FieldSymbols'; +import { InkTool } from '../../../../fields/InkField'; +import { NumCast, StrCast } from '../../../../fields/Types'; +import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { Docs } from '../../../documents/Documents'; +import { DragManager } from '../../../util/DragManager'; +import { SnappingManager } from '../../../util/SnappingManager'; +import { UndoManager } from '../../../util/UndoManager'; +import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; +import { Colors } from '../../global/globalEnums'; +import { MarqueeAnnotator } from '../../MarqueeAnnotator'; +import { AnchorMenu } from '../../pdf/AnchorMenu'; +import { Annotation } from '../../pdf/Annotation'; +import { SidebarAnnos } from '../../SidebarAnnos'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { PinProps } from '../trails'; +import './MapBox.scss'; +import { MapBoxInfoWindow } from './MapBoxInfoWindow'; + +/** + * MapBox architecture: + * Main component: MapBox.tsx + * Supporting Components: SidebarAnnos, CollectionStackingView + * + * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content. + * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view. + * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available, + * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map). + * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts). + * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps + */ + +// const _global = (window /* browser */ || global /* node */) as any; + +const mapContainerStyle = { + height: '100%', +}; + +const defaultCenter = { + 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); + +/** + * Consider integrating later: allows for drawing, circling, making shapes on map + */ +// const drawingManager = new window.google.maps.drawing.DrawingManager({ +// drawingControl: true, +// drawingControlOptions: { +// position: google.maps.ControlPosition.TOP_RIGHT, +// drawingModes: [ +// google.maps.drawing.OverlayType.MARKER, +// // currently we are not supporting the following drawing mode on map, a thought for future development +// google.maps.drawing.OverlayType.CIRCLE, +// google.maps.drawing.OverlayType.POLYLINE, +// ], +// }, +// }); + +// options for searchbox in Google Maps Places Autocomplete API +const options = { + fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields + strictBounds: false, + types: ['establishment'], // type pf places, subject of change according to user need +} as google.maps.places.AutocompleteOptions; + +@observer +export class MapBox2 extends ViewBoxAnnotatableComponent>() { + static UseBing = true; + private _dropDisposer?: DragManager.DragDropDisposer; + private _disposers: { [name: string]: IReactionDisposer } = {}; + private _annotationLayer: React.RefObject = React.createRef(); + @observable private _overlayAnnoInfo: Opt; + showInfo = action((anno: Opt) => (this._overlayAnnoInfo = anno)); + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(MapBox2, fieldKey); + } + public get SidebarKey() { + return this.fieldKey + '-sidebar'; + } + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); + @computed get inlineTextAnnotations() { + return this.allMapMarkers.filter(a => a.textInlineAnnotations); + } + + @observable private _map: google.maps.Map = null as unknown as google.maps.Map; + @observable private selectedPlace: Doc | undefined; + @observable private markerMap: { [id: string]: google.maps.Marker } = {}; + @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter; + @observable private _marqueeing: number[] | undefined; + @observable private _isAnnotating = false; + @observable private inputRef = React.createRef(); + @observable private searchMarkers: google.maps.Marker[] = []; + @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); + @observable private _savedAnnotations = new ObservableMap(); + @computed get allSidebarDocs() { + return DocListCast(this.dataDoc[this.SidebarKey]); + } + @computed get allMapMarkers() { + return DocListCast(this.dataDoc[this.annotationKey]); + } + @observable private toggleAddMarker = false; + private _mainCont: React.RefObject = React.createRef(); + + @observable _showSidebar = false; + @computed get SidebarShown() { + return this._showSidebar || this.layoutDoc._showSidebar ? true : false; + } + + static _canAnnotate = true; + static _hadSelection: boolean = false; + private _sidebarRef = React.createRef(); + private _ref: React.RefObject = React.createRef(); + + componentDidMount() { + this.props.setContentView?.(this); + } + + @action + private setSearchBox = (searchBox: any) => { + this.searchBox = searchBox; + }; + + // iterate allMarkers to size, center, and zoom map to contain all markers + private fitBounds = (map: google.maps.Map) => { + const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds(); + const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean); + !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds())); + }; + + /** + * Custom control for add marker button + * @param controlDiv + * @param map + */ + private CenterControl = () => { + const controlDiv = document.createElement('div'); + controlDiv.className = 'mapBox-addMarker'; + // Set CSS for the control border. + const controlUI = document.createElement('div'); + controlUI.style.backgroundColor = '#fff'; + controlUI.style.borderRadius = '3px'; + controlUI.style.cursor = 'pointer'; + controlUI.style.marginTop = '10px'; + controlUI.style.borderRadius = '4px'; + controlUI.style.marginBottom = '22px'; + controlUI.style.textAlign = 'center'; + controlUI.style.position = 'absolute'; + controlUI.style.width = '32px'; + controlUI.style.height = '32px'; + controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.'; + + const plIcon = document.createElement('img'); + plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png'; + plIcon.style.color = 'rgb(25,25,25)'; + plIcon.style.fontFamily = 'Roboto,Arial,sans-serif'; + plIcon.style.fontSize = '16px'; + plIcon.style.lineHeight = '32px'; + plIcon.style.left = '18'; + plIcon.style.top = '15'; + plIcon.style.position = 'absolute'; + plIcon.width = 14; + plIcon.height = 14; + plIcon.innerHTML = 'Add'; + controlUI.appendChild(plIcon); + + // Set CSS for the control interior. + const markerIcon = document.createElement('img'); + markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png'; + markerIcon.style.color = 'rgb(25,25,25)'; + markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif'; + markerIcon.style.fontSize = '16px'; + markerIcon.style.lineHeight = '32px'; + markerIcon.style.left = '-2'; + markerIcon.style.top = '1'; + markerIcon.width = 30; + markerIcon.height = 30; + markerIcon.style.position = 'absolute'; + markerIcon.innerHTML = 'Add'; + controlUI.appendChild(markerIcon); + + // Setup the click event listeners + controlUI.addEventListener('click', () => { + if (this.toggleAddMarker === true) { + this.toggleAddMarker = false; + console.log('add marker button status:' + this.toggleAddMarker); + controlUI.style.backgroundColor = '#fff'; + markerIcon.style.color = 'rgb(25,25,25)'; + } else { + this.toggleAddMarker = true; + console.log('add marker button status:' + this.toggleAddMarker); + controlUI.style.backgroundColor = '#4476f7'; + markerIcon.style.color = 'rgb(255,255,255)'; + } + }); + controlDiv.appendChild(controlUI); + return controlDiv; + }; + + /** + * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list + * @param position - the LatLng position where the marker is placed + * @param map + */ + @action + private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => { + const marker = new google.maps.Marker({ + position: position, + map: map, + }); + map.panTo(position); + const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); + this.addDocument(mapMarker, this.annotationKey); + }; + + _loadPending = true; + /** + * store a reference to google map instance + * setup the drawing manager on the top right corner of map + * fit map bounds to contain all markers + * @param map + */ + @action + private loadHandler = (map: google.maps.Map) => { + this._map = map; + this._loadPending = true; + const centerControlDiv = this.CenterControl(); + map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv); + //drawingManager.setMap(map); + // if (navigator.geolocation) { + // navigator.geolocation.getCurrentPosition( + // (position: Position) => { + // const pos = { + // lat: position.coords.latitude, + // lng: position.coords.longitude, + // }; + // this._map.setCenter(pos); + // } + // ); + // } else { + // alert("Your geolocation is not supported by browser.") + // }; + map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5)); + map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng))); + setTimeout(() => { + if (this._loadPending && this._map.getBounds()) { + this._loadPending = false; + this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + } + }, 250); + // listener to addmarker event + this._map.addListener('click', (e: MouseEvent) => { + if (this.toggleAddMarker === true) { + this.placeMarker((e as any).latLng, map); + } + }); + }; + + @action + centered = () => { + if (this._loadPending && this._map.getBounds()) { + this._loadPending = false; + this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + } + this.dataDoc.mapLat = this._map.getCenter()?.lat(); + this.dataDoc.mapLng = this._map.getCenter()?.lng(); + }; + + @action + zoomChanged = () => { + if (this._loadPending && this._map.getBounds()) { + this._loadPending = false; + this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + } + this.dataDoc.mapZoom = this._map.getZoom(); + }; + + /** + * Load and render all map markers + * @param marker + * @param place + */ + @action + private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => { + place[Id] ? (this.markerMap[place[Id]] = marker) : null; + }; + + /** + * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true + * @param e + * @param place + */ + @action + private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => { + // set which place was clicked + this.selectedPlace = place; + place.infoWindowOpen = true; + }; + + /** + * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts + * @param doc + * @param sidebarKey + * @returns + */ + sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { + console.log('print all sidebar Docs'); + if (!this.layoutDoc._showSidebar) this.toggleSidebar(); + const docs = doc instanceof Doc ? [doc] : doc; + docs.forEach(doc => { + 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.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {}); + this.addDocument(marker, this.annotationKey); + } + } + }); //add to annotation list + + return this.addDocument(doc, sidebarKey); // add to sidebar list + }; + + /** + * Removing documents from the sidebar + * @param doc + * @param sidebarKey + * @returns + */ + sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => { + if (this.layoutDoc._showSidebar) this.toggleSidebar(); + const docs = doc instanceof Doc ? [doc] : doc; + return this.removeDocument(doc, sidebarKey); + }; + + /** + * Toggle sidebar onclick the tiny comment button on the top right corner + * @param e + */ + sidebarBtnDown = (e: React.PointerEvent) => { + setupMoveUpEvents( + this, + e, + (e, down, delta) => + runInAction(() => { + const localDelta = this.props + .ScreenToLocalTransform() + .scale(this.props.NativeDimScaling?.() || 1) + .transformDirection(delta[0], delta[1]); + const fullWidth = this.layoutDoc[WidthSym](); + const mapWidth = fullWidth - this.sidebarWidth(); + if (this.sidebarWidth() + localDelta[0] > 0) { + this._showSidebar = true; + this.layoutDoc._width = fullWidth + localDelta[0]; + this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; + } else { + this._showSidebar = false; + this.layoutDoc._width = mapWidth; + this.layoutDoc._sidebarWidthPercent = '0%'; + } + return false; + }), + emptyFunction, + () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map') + ); + }; + + sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); + @computed get sidebarWidthPercent() { + return StrCast(this.layoutDoc._sidebarWidthPercent, '0%'); + } + @computed get sidebarColor() { + return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4')); + } + + /** + * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted; + * add a customized temporary marker on the map + */ + @action + private handlePlaceChanged = () => { + const place = this.searchBox.getPlace(); + + if (!place.geometry || !place.geometry.location) { + // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed + window.alert("No details available for input: '" + place.name + "'"); + return; + } + + // zoom in on the location of the search result + if (place.geometry.viewport) { + this._map.fitBounds(place.geometry.viewport); + } else { + this._map.setCenter(place.geometry.location); + this._map.setZoom(17); + } + + // customize icon => customized icon for the nature of the location selected + const icon = { + url: place.icon as string, + size: new google.maps.Size(71, 71), + origin: new google.maps.Point(0, 0), + anchor: new google.maps.Point(17, 34), + scaledSize: new google.maps.Size(25, 25), + }; + + // put temporary cutomized marker on searched location + this.searchMarkers.forEach(marker => { + marker.setMap(null); + }); + this.searchMarkers = []; + this.searchMarkers.push( + new window.google.maps.Marker({ + map: this._map, + icon, + title: place.name, + position: place.geometry.location, + }) + ); + }; + + /** + * Handles toggle of sidebar on click the little comment button + */ + @computed get sidebarHandle() { + return ( +
+ +
+ ); + } + + // TODO: Adding highlight box layer to Maps + @action + toggleSidebar = () => { + //1.2 * w * ? = .2 * w .2/1.2 + const prevWidth = this.sidebarWidth(); + this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; + this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); + }; + + sidebarDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); + }; + sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => { + const bounds = this._ref.current!.getBoundingClientRect(); + this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%'; + this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%'; + e.preventDefault(); + return false; + }; + + setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func); + + @action + onMarqueeDown = (e: React.PointerEvent) => { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + setupMoveUpEvents( + this, + e, + action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), + returnFalse, + () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), + false + ); + } + }; + @action finishMarquee = (x?: number, y?: number) => { + this._marqueeing = undefined; + this._isAnnotating = false; + x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false); + }; + + addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => { + return this.addDocument(doc, annotationKey); + }; + + pointerEvents = () => { + return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'; + }; + @computed get annotationLayer() { + return ( +
+ {this.inlineTextAnnotations + .sort((a, b) => NumCast(a.y) - NumCast(b.y)) + .map(anno => ( + + ))} +
+ ); + } + + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; + + /** + * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker + * @returns + */ + private renderMarkers = () => { + return this.allMapMarkers.map(place => ( + this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} /> + )); + }; + + // TODO: auto center on select a document in the sidebar + private handleMapCenter = (map: google.maps.Map) => { + // console.log("print the selected views in selectionManager:") + // if (SelectionManager.Views().lastElement()) { + // console.log(SelectionManager.Views().lastElement()); + // } + }; + + 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()]; + infoWidth = () => this.props.PanelWidth() / 5; + infoHeight = () => 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(), + }); + } + }); + }; + + + + + bingSearchBarContents: any = "Boston, MA"; // For Bing Maps: The contents of the Bing search bar (string) + + /* + * For Bing Maps + * Called by search button's onClick + * Finds the geocode of the searched contents and sets location to that location + **/ + @action + bingSearch = async() =>{ + + // clear all pins + this._bingMap.current.entities.clear(); + + const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); + + // this.rootDoc.latitude =location.latitude; + // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info? + //DocComponents file is where rootDoc is + + // call a helper method that updates the this._bingMap.current.setView, + // replaces this method call below + this._bingMap.current.setView({ + center: new this.MicrosoftMaps.Location(location.latitude, location.longitude), + // zoom: , + }); + + //Create custom Pushpin + var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { + title: this.bingSearchBarContents, + subTitle: 'subtitle here', + text: '1' + }); + + //Add the pushpin to the map + this._bingMap.current.entities.push(pin); + // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {}); + // this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc + } + + + // /** + // * For Bing Maps + // * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list + // * @param position - the LatLng position where the marker is placed + // * @param map + // */ + // @action + // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => { + // const marker = new google.maps.Marker({ + // position: position, + // map: map, + // }); + // map.panTo(position); + // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); + // this.addDocument(mapMarker, this.annotationKey); + // }; + + + + + + bingViewOptions = { + center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude + mapTypeId: 'grayscale', + }; + bingMapOptions = { + navigationBarMode: 'square', + }; + bingMapReady = (map: any) => (this._bingMap = map.map); + render() { + const renderAnnotations = (docFilters?: () => string[]) => null; + return ( +
+
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} + + + + {!MapBox2.UseBing ? null : + this.bingSearchBarContents = newText} + placeholder="..." + size="medium" + text="Boston, MA" + onKeyPress={e => console.log(e.key)} + />} + + {!MapBox2.UseBing ? null : + } + + + + + {!MapBox2.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 : ( + + )} +
+ {/* */} +
+ +
+ {this.sidebarHandle} +
+ ); + } +} -- cgit v1.2.3-70-g09d2 From 5185db43b2e48f049690fadcee0081aca634cf4d Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 5 Apr 2023 15:13:33 -0400 Subject: Added infobox after pushpin clicked --- src/client/views/nodes/MapBox/MapBox.tsx | 93 ++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index b01426bcd..cc7d12128 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -606,16 +606,64 @@ export class MapBox extends ViewBoxAnnotatableComponent { + this._bingMap.current.setView({ + center: new this.MicrosoftMaps.Location(this.rootDoc.latitude, this.rootDoc.longitude), + // zoom: location + }); + } + + infobox:any; + + @action + createPushpin = (latitude:number, longitude:number) =>{ + var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(latitude, longitude), { + title: this.bingSearchBarContents, + subTitle: 'subtitle here', + text: '1' + }); + + //Create an infobox at the pin + this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(latitude, longitude), { + visible: false + }); + + //Assign the infobox to a map instance. + this.infobox.setMap(this._bingMap.current); + //Store some metadata with the pushpin. + pin.metadata = { + title: 'Pin Title', + description: 'Pin discription' + }; + //Add a click event handler to the pushpin. + this.MicrosoftMaps.Events.addHandler(pin, 'click', this.pushpinClicked); + //Add pushpin to the map. + this._bingMap.current.entities.push(pin); + } + + + pushpinClicked = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }) => { + //Make sure the infobox has metadata to display. + if (e.target.metadata) { + //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e? + this.infobox.setOptions({ + location: e.target.getLocation(), + title: e.target.metadata.title, + description: e.target.metadata.description, + visible: true + }); + } + } + /* * For Bing Maps * Called by search button's onClick @@ -631,17 +679,21 @@ export class MapBox extends ViewBoxAnnotatableComponent console.log(e.key)} + onKeyPress={(e: { key: any; }) => console.log(e.key)} />} {!MapBox.UseBing ? null : -- cgit v1.2.3-70-g09d2 From e1494deb519cbd492be6cfe413b886bdfbb9404e Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 5 Apr 2023 17:30:59 -0400 Subject: Working on pushpin and infowindow datadoc stuff --- src/client/views/nodes/MapBox/MapBox.tsx | 95 ++++++++++++++++--------------- src/client/views/nodes/MapBox/MapBox2.tsx | 20 ++++++- 2 files changed, 67 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index cc7d12128..ac26fbe08 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -604,9 +604,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { this._bingMap.current.setView({ - center: new this.MicrosoftMaps.Location(this.rootDoc.latitude, this.rootDoc.longitude), + center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude), // zoom: location }); } - infobox:any; + infobox:any; @action createPushpin = (latitude:number, longitude:number) =>{ var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(latitude, longitude), { @@ -641,13 +639,17 @@ export class MapBox extends ViewBoxAnnotatableComponent{ //TODO: PlaceResult, searching more formally + bingSearch = async() =>{ //TODO: searching more formally // clear all pins // this._bingMap.current.entities.clear(); const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); - this.rootDoc.latitude = location.latitude; - this.rootDoc.longitude = location.longitude; + this.dataDoc.latitude = location.latitude; + this.dataDoc.longitude = location.longitude; this.updateView(); - - - - //Create custom Pushpin TODO: MAKE THIS ITS OWN METHOD + // Each marker be its own document -- pin and info + this.createPushpin(location.latitude, location.longitude); @@ -692,6 +699,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { - // const pin = new this.MicrosoftMaps.Pushpin(location, { - // title: this.bingSearchBarContents, - // subTitle: 'subtitle here', - // text: '1' - // }); - - // this._bingMap.current.panTo(location); - // this._bingMap.current.entities.push(pin); - - // // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(location.latitude), NumCast(location.longitude), false, [], {}); - // // this.addDocument(mapMarker, this.annotationKey); - // }; - @@ -774,17 +763,20 @@ export class MapBox extends ViewBoxAnnotatableComponent (this._bingMap = map.map); render() { const renderAnnotations = (docFilters?: () => string[]) => null; @@ -826,10 +818,23 @@ export class MapBox extends ViewBoxAnnotatableComponent} - - - {!MapBox.UseBing ? null : } + {this.allMapMarkers + .filter(marker => marker.infoWindowOpen) + .map(marker => ( + + ))} +
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx index c11f76439..4c28d4df1 100644 --- a/src/client/views/nodes/MapBox/MapBox2.tsx +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -703,11 +703,25 @@ export class MapBox2 extends ViewBoxAnnotatableComponent} - - - + {!MapBox2.UseBing ? null : } + {this.allMapMarkers + .filter(marker => marker.infoWindowOpen) + .map(marker => ( + + ))} +
-- cgit v1.2.3-70-g09d2 From b703dc034c84ecc2842f34b562097ee253ba09ac Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 6 Apr 2023 10:24:55 -0400 Subject: Working on mapboxinfowindow integration --- src/client/views/nodes/MapBox/MapBox.tsx | 180 ++++++++++++++----------------- 1 file changed, 81 insertions(+), 99 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index ac26fbe08..1892a5b61 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -624,29 +624,6 @@ export class MapBox extends ViewBoxAnnotatableComponent{ - var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(latitude, longitude), { - title: this.bingSearchBarContents, - subTitle: 'subtitle here', - text: '1' - }); - - //Create an infobox at the pin - this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(latitude, longitude), { - visible: false - }); - - //Assign the infobox to a map instance. - this.infobox.setMap(this._bingMap.current); - //Store some metadata with the pushpin. - pin.metadata = { - title: this.bingSearchBarContents, - description: 'Pin description' - }; - //Add a click event handler to the pushpin. - this.MicrosoftMaps.Events.addHandler(pin, 'click', this.pushpinClicked); - //Add pushpin to the map. - this._bingMap.current.entities.push(pin); - // Stores the pushpin as a MapMarkerDocument const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {}); this.addDocument(mapMarker, this.annotationKey); @@ -666,6 +643,13 @@ export class MapBox extends ViewBoxAnnotatableComponent any; }; }, pin: Doc) => { + // // set which place was clicked + // this.selectedPlace = place; + pin.infoWindowOpen = true; + } + /** * Returns a list of MapMarkerDocument */ @@ -682,81 +666,73 @@ export class MapBox extends ViewBoxAnnotatableComponent{ //TODO: searching more formally // clear all pins - // this._bingMap.current.entities.clear(); + this._bingMap.current.entities.clear(); + //TODO: clear all infoboxes const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); this.dataDoc.latitude = location.latitude; this.dataDoc.longitude = location.longitude; this.updateView(); - // Each marker be its own document -- pin and info - - this.createPushpin(location.latitude, location.longitude); - - // // Adds all pins to the map - // for (let i = 0; i < temp.length; i++) { - // this._bingMap.current.entities.push(temp[i]); - // } - } - // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(location.latitude), NumCast(location.longitude), false, [], {}); - // this.addDocument(mapMarker, this.annotationKey); - - - // this.MicrosoftMaps.SpatialDataService.GeoDataAPIManager.getBoundary( - // this._bingMap.current.getCenter(), - // this.geoDataRequestOptions, - // this._bingMap.current, - // function (data) { - // if (data.results && data.results.length > 0) { - // map.entities.push(data.results[0].Polygons); - // } - // }, - // null, - // function errCallback(networkStatus, statusMessage) { - // console.log(networkStatus); - // console.log(statusMessage); - // } - // ); - // _loadPending = true; - // /** - // * store a reference to google map instance - // * setup the drawing manager on the top right corner of map - // * fit map bounds to contain all markers - // * - // */ - // @action - // private loadHandler = () => { - - // // this._loadPending = true; - - // // // for making GoogleMap markers - // // // const centerControlDiv = this.CenterControl(); - // // // map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv); + // // Creates a temporary pin but does not add it to the dataDoc, UNCOMMENT LATER + // var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { + // title: this.bingSearchBarContents, + // subTitle: 'subtitle here', + // text: '1', + // color: 'blue' + // }); + // this._bingMap.current.entities.push(pin); - // // // this._bingMap.current. + this.createPushpin(location.latitude, location.longitude); // Creates an actual pushpin + this.addAllPins(); // Adds all pushpins in the datadoc onto the map - // // map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5)); - // // map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng))); - // // setTimeout(() => { - // // if (this._loadPending && this._map.getBounds()) { - // // this._loadPending = false; - // // this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); - // // } - // // }, 250); + console.log(this.allMapPushpins + .filter(marker => marker.infoWindowOpen) + .length) + } - // // // listener to addmarker event, creates pushpin onClick - // // this._bingMap.addListener('click', (e: MouseEvent) => { - // // if (this.toggleAddMarker === true) { - // // this.placeMarker((e as any).latLng, map); //TODO: Implement placeMarker - // // } - // // }); - // }; + /** + * Adds all pushpins in dataDoc onto the map + */ + @action + addAllPins = () =>{ + this.allMapPushpins.map(pin => this.createInfobox(pin)); + } + @action + createInfobox = (pin:any) =>{ + var pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { + title: this.bingSearchBarContents, + subTitle: 'subtitle here', + text: '1' + }); + this._bingMap.current.entities.push(pushPin); + //Create an infobox at the pin + this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { + visible: false + }); + //Assign the infobox to a map instance. + this.infobox.setMap(this._bingMap.current); + //Store some metadata with the pushpin. + pushPin.metadata = { + title: pushPin.title, + description: 'Pin description' + }; + //Add a click event handler to the pushpin. + // For bing maps infobox + // this.MicrosoftMaps.Events.addHandler(pushPin, 'click', this.pushpinClicked); + + // For our infowindow + this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e:any) => this.pushpinClicked2(e, pushPin)); + + } + + /** @@ -818,22 +794,28 @@ export class MapBox extends ViewBoxAnnotatableComponent} - {!MapBox.UseBing ? null : } - {this.allMapMarkers - .filter(marker => marker.infoWindowOpen) - .map(marker => ( - - ))} + {!MapBox.UseBing ? null : + + {this.allMapPushpins + .filter(marker => marker.infoWindowOpen) + .map(marker => ( + console.log('this is a marker window') + // + ))} + + + } +
-- cgit v1.2.3-70-g09d2 From 4e2087a3d86ff2166ac4bca31d77850e3052ecfc Mon Sep 17 00:00:00 2001 From: Eric Date: Sat, 8 Apr 2023 17:42:19 -0400 Subject: Started on place pin mode --- src/client/views/nodes/MapBox/MapBox.tsx | 117 +++++++++++++++++++------------ 1 file changed, 74 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 1892a5b61..ef45cdd1a 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -621,15 +621,19 @@ export class MapBox extends ViewBoxAnnotatableComponent{ // Stores the pushpin as a MapMarkerDocument + const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {}); this.addDocument(mapMarker, this.annotationKey); + // mapMarker.infoWindowOpen = true; + console.log("original:" + mapMarker) } + infobox:any; pushpinClicked = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }) => { //Make sure the infobox has metadata to display. if (e.target.metadata) { @@ -648,6 +652,7 @@ export class MapBox extends ViewBoxAnnotatableComponent{ //TODO: searching more formally - - // clear all pins - this._bingMap.current.entities.clear(); - //TODO: clear all infoboxes - - const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); - - this.dataDoc.latitude = location.latitude; - this.dataDoc.longitude = location.longitude; - this.updateView(); - - - - // // Creates a temporary pin but does not add it to the dataDoc, UNCOMMENT LATER - // var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { - // title: this.bingSearchBarContents, - // subTitle: 'subtitle here', - // text: '1', - // color: 'blue' - // }); - // this._bingMap.current.entities.push(pin); - - this.createPushpin(location.latitude, location.longitude); // Creates an actual pushpin - this.addAllPins(); // Adds all pushpins in the datadoc onto the map - - console.log(this.allMapPushpins - .filter(marker => marker.infoWindowOpen) - .length) - } - - /** - * Adds all pushpins in dataDoc onto the map - */ - @action - addAllPins = () =>{ - this.allMapPushpins.map(pin => this.createInfobox(pin)); - } + @action + bingSearch = async() =>{ + + // clear all pins + this._bingMap.current.entities.clear(); + //TODO: clear all infoboxes + + const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); + + this.dataDoc.latitude = location.latitude; + this.dataDoc.longitude = location.longitude; + this.updateView(); + + + + // // Creates a temporary pin but does not add it to the dataDoc, UNCOMMENT LATER + // var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { + // title: this.bingSearchBarContents, + // subTitle: 'subtitle here', + // text: '1', + // color: 'blue' + // }); + // this._bingMap.current.entities.push(pin); + + this.createPushpin(location.latitude, location.longitude); // Creates an actual pushpin + this.addAllPins(); // Adds all pushpins in the datadoc onto the map + + console.log(this.allMapPushpins + .filter(marker => marker.infoWindowOpen) + .length) + console.log(this.allMapPushpins + .map(marker => console.log(marker.infoWindowOpen))) + } + + /** + * Adds all pushpins in dataDoc onto the map + */ + @action + addAllPins = () =>{ + this.allMapPushpins.map(pin => this.createInfobox(pin)); + } @action createInfobox = (pin:any) =>{ @@ -720,8 +727,9 @@ export class MapBox extends ViewBoxAnnotatableComponent{ + if (this.placePinOn) + this.placePinOn = false; + else this.placePinOn = true; + } + + bingMapReady = (map: any) => (this._bingMap = map.map); render() { const renderAnnotations = (docFilters?: () => string[]) => null; @@ -793,6 +814,16 @@ export class MapBox extends ViewBoxAnnotatableComponent} + + {!MapBox.UseBing ? null : + this.placePinOn ? + + : + + } + {!MapBox.UseBing ? null : -- cgit v1.2.3-70-g09d2 From 1925afd8a5e2cea4fcb6d2e525264967a20f6841 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 10 Apr 2023 16:05:55 -0400 Subject: Pre-bob --- src/client/views/nodes/MapBox/MapBox.tsx | 83 ++++++++++++++++------ src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 4 +- 2 files changed, 64 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index ef45cdd1a..902aa2d13 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -239,7 +239,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { // set which place was clicked this.selectedPlace = place; - place.infoWindowOpen = true; + // place.infoWindowOpen = true; }; /** @@ -629,7 +629,6 @@ export class MapBox extends ViewBoxAnnotatableComponent any; }; }) => { + //Make sure the infobox has metadata to display. + if (e.target.metadata) { + //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e? + this.infobox.setOptions({ + location: e.target.getLocation(), + title: e.target.metadata.title, + description: e.target.metadata.description, + visible: true + }); + } + } //PushpinClicked using MapBoxInfoWindow private pushpinClicked2 = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }, pin: Doc) => { // // set which place was clicked // this.selectedPlace = place; pin.infoWindowOpen = true; - console.log("later:" + pin.infoWindowOpen) + // console.log("later:" + pin.infoWindowOpen) } /** @@ -661,7 +672,20 @@ export class MapBox extends ViewBoxAnnotatableComponent{ + if(this.placePinOn){ + this.createPushpin(e.location.latitude, e.location.longitude); + this.addAllPins(); + this.placePinOn = false; + } + } + + searched_pin:any; /* * For Bing Maps * Called by search button's onClick @@ -669,10 +693,8 @@ export class MapBox extends ViewBoxAnnotatableComponent{ - - // clear all pins - this._bingMap.current.entities.clear(); - //TODO: clear all infoboxes + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick); + this.addAllPins(); const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); @@ -680,19 +702,19 @@ export class MapBox extends ViewBoxAnnotatableComponent marker.infoWindowOpen) @@ -706,15 +728,20 @@ export class MapBox extends ViewBoxAnnotatableComponent{ + this._bingMap.current.entities.clear(); + //TODO: clear all infoboxes + if(this.searched_pin) + this._bingMap.current.entities.push(this.searched_pin); this.allMapPushpins.map(pin => this.createInfobox(pin)); + this.allMapPushpins.map(pin => this._bingMap.current.entities.push(pin)); } @action createInfobox = (pin:any) =>{ var pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { - title: this.bingSearchBarContents, - subTitle: 'subtitle here', - text: '1' + // title: this.bingSearchBarContents, + // subTitle: 'subtitle here', + // text: '1' }); this._bingMap.current.entities.push(pushPin); @@ -737,6 +764,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked2(e, pushPin)); + this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e)); } @@ -846,6 +874,17 @@ export class MapBox extends ViewBoxAnnotatableComponent } + {/* */} diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx index 00bedafbe..08ed7b40f 100644 --- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx +++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx @@ -47,7 +47,9 @@ export class MapBoxInfoWindow extends React.Component (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean); render() { return ( - +
Date: Mon, 10 Apr 2023 18:24:31 -0400 Subject: Post-bob --- src/client/views/nodes/MapBox/MapBox.tsx | 93 +++++++++++--------------------- 1 file changed, 32 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 902aa2d13..2298ddc60 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -5,6 +5,7 @@ import { EditableText } from 'browndash-components'; import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; @@ -628,7 +629,7 @@ export class MapBox extends ViewBoxAnnotatableComponent any; }; }) => { - //Make sure the infobox has metadata to display. - if (e.target.metadata) { - //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e? - this.infobox.setOptions({ - location: e.target.getLocation(), - title: e.target.metadata.title, - description: e.target.metadata.description, - visible: true - }); - } - } //PushpinClicked using MapBoxInfoWindow private pushpinClicked2 = (e: { target: { metadata: { title: any; description: any; }; getLocation: () => any; }; }, pin: Doc) => { @@ -716,11 +705,11 @@ export class MapBox extends ViewBoxAnnotatableComponent marker.infoWindowOpen) - .length) - console.log(this.allMapPushpins - .map(marker => console.log(marker.infoWindowOpen))) + // console.log(this.allMapPushpins + // .filter(marker => marker.infoWindowOpen) + // .length) + // console.log(this.allMapPushpins + // .map(marker => console.log(marker.infoWindowOpen))) } /** @@ -744,11 +733,22 @@ export class MapBox extends ViewBoxAnnotatableComponent`, visible: false }); + const root = ReactDOM.createRoot(document.getElementById(id)!); + root.render(
omg
); + + //Assign the infobox to a map instance. this.infobox.setMap(this._bingMap.current); //Store some metadata with the pushpin. @@ -763,8 +763,8 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked2(e, pushPin)); - this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e)); + this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e:any) => this.pushpinClicked(e)); + // this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e)); } @@ -829,7 +829,6 @@ export class MapBox extends ViewBoxAnnotatableComponent this.bingSearchBarContents = newText} @@ -858,21 +857,20 @@ export class MapBox extends ViewBoxAnnotatableComponent marker.infoWindowOpen) .map(marker => ( - console.log('this is a marker window') - // + // console.log('this is a marker window') + ))} - } {/* */} - -
- - - 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 : ( Date: Wed, 12 Apr 2023 22:45:03 -0400 Subject: amongus --- src/client/views/nodes/MapBox/MapBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 2298ddc60..51b88fe5e 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -745,8 +745,8 @@ export class MapBox extends ViewBoxAnnotatableComponent`, visible: false }); - const root = ReactDOM.createRoot(document.getElementById(id)!); - root.render(
omg
); + // const root = ReactDOM.createRoot(document.getElementById(id)!); + // root.render(
omg
); //Assign the infobox to a map instance. -- cgit v1.2.3-70-g09d2 From 4aa933c5b5da13cd10fe427b8513523b2e1d825f Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 12 Apr 2023 22:46:40 -0400 Subject: amongus2 --- src/client/views/nodes/MapBox/MapBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 51b88fe5e..d0eb28f91 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -26,7 +26,7 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps } from '../trails'; import './MapBox.scss'; import { MapBoxInfoWindow } from './MapBoxInfoWindow'; - +// amongus /** * MapBox architecture: * Main component: MapBox.tsx -- cgit v1.2.3-70-g09d2 From f912a233a89c8772b22b71d34830ff4b0ba82310 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 4 May 2023 14:03:21 -0400 Subject: post-demo --- src/client/views/nodes/MapBox/MapBox.tsx | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index d0eb28f91..fed4535cb 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -26,6 +26,7 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps } from '../trails'; import './MapBox.scss'; import { MapBoxInfoWindow } from './MapBoxInfoWindow'; +import { ReactDOMServer } from "react"; // amongus /** * MapBox architecture: @@ -626,7 +627,6 @@ export class MapBox extends ViewBoxAnnotatableComponent{ // Stores the pushpin as a MapMarkerDocument - const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {}); this.addDocument(mapMarker, this.annotationKey); mapMarker.infoWindowOpen = true; @@ -670,6 +670,7 @@ export class MapBox extends ViewBoxAnnotatableComponent) + // var htmlString = ReactDOMServer.renderToString(
); + + //Create an infobox at the pin this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { - htmlContent: `
`, + htmlContent: infoboxTemplate, // style="width:100; height:100; background:blue" visible: false }); - // const root = ReactDOM.createRoot(document.getElementById(id)!); - // root.render(
omg
); + try{ + // console.log(document.getElementById(id)) + // console.log(this.infobox) + // const root = ReactDOM.createRoot(document.getElementById(id)!); + // root.render(
); + } + catch(e){ + console.log(e) + } + //Assign the infobox to a map instance. -- cgit v1.2.3-70-g09d2 From 480f155d69bd5546f04f0a9f5a78f026f8b6533a Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 10 May 2023 10:44:30 -0400 Subject: formatting mapbox --- package-lock.json | 39 +++++++ src/client/views/nodes/MapBox/MapBox.scss | 147 +++++++++++++------------- src/client/views/nodes/MapBox/MapBox.tsx | 71 +++++-------- src/client/views/nodes/MapBox/MapBox2.tsx | 168 ++---------------------------- 4 files changed, 151 insertions(+), 274 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 196906718..111d1838a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5597,6 +5597,16 @@ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "d3": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz", @@ -6977,6 +6987,28 @@ "is-symbol": "^1.0.2" } }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", @@ -6988,6 +7020,7 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { + "d": "^1.0.1", "ext": "^1.1.2" } }, @@ -22660,6 +22693,12 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss index fb15520f6..539c506c7 100644 --- a/src/client/views/nodes/MapBox/MapBox.scss +++ b/src/client/views/nodes/MapBox/MapBox.scss @@ -1,87 +1,92 @@ -@import "../../global/globalCssVariables.scss"; +@import '../../global/globalCssVariables.scss'; .mapBox { - width: 100%; - height: 100%; - overflow: hidden; - display: flex; + width: 100%; + height: 100%; + overflow: hidden; + display: flex; - .mapBox-infoWindow { - background-color: white; - opacity: 0.75; - padding: 12; - font-size: 17; - } + .mapBox-infoWindow { + background-color: white; + opacity: 0.75; + padding: 12; + font-size: 17; + } - .mapBox-overlayButton-sidebar { - background: #121721; - height: 25px; - width: 25px; - right: 5px; - display: flex; - position: absolute; - align-items: center; - justify-content: center; - border-radius: 3px; - pointer-events: all; - z-index: 1; // so it appears on top of the document's title, if shown - - box-shadow: $standard-box-shadow; - transition: 0.2s; - - &:hover{ - filter: brightness(0.85); - } - } + .mapBox-overlayButton-sidebar { + background: #121721; + height: 25px; + width: 25px; + right: 5px; + display: flex; + position: absolute; + align-items: center; + justify-content: center; + border-radius: 3px; + pointer-events: all; + z-index: 1; // so it appears on top of the document's title, if shown - .mapBox-wrapper { - width: 100%; - .mapBox-input { - box-sizing: border-box; - border: 1px solid transparent; - width: 240px; - height: 32px; - padding: 0 12px; - border-radius: 3px; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); - font-size: 14px; - outline: none; - text-overflow: ellipses; - position: absolute; - left: 50%; - margin-left: -120px; - } - } + box-shadow: $standard-box-shadow; + transition: 0.2s; - .mapBox-sidebar-handle { - top: 0; - //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views - width: 10px; - height: 100%; - max-height: 35px; - background: lightgray; - border-radius: 20px; - cursor:grabbing; + &:hover { + filter: brightness(0.85); } - .mapBox-addMarker { + } + + .mapBox-wrapper { + width: 100%; + .mapBox-input { + box-sizing: border-box; + border: 1px solid transparent; + width: 240px; + height: 32px; + padding: 0 12px; + border-radius: 3px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + font-size: 14px; + outline: none; + text-overflow: ellipses; + position: absolute; left: 50%; - margin-left: 120px; - right: unset !important; - margin-top: -10; - height: max-content; - } - .searchbox { - display:none; - } - .mapBox-addMarker { - display:none; + margin-left: -120px; } + } + .mapBox-sidebar { + position: absolute; + right: 0; + height: 100%; + } + + .mapBox-sidebar-handle { + top: 0; + //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views + width: 10px; + height: 100%; + max-height: 35px; + background: lightgray; + border-radius: 20px; + cursor: grabbing; + } + .mapBox-addMarker { + left: 50%; + margin-left: 120px; + right: unset !important; + margin-top: -10; + height: max-content; + } + .searchbox { + display: none; + } + .mapBox-addMarker { + display: none; + } } .mapBox:hover { .mapBox-addMarker { - display:block; + display: block; } .searchbox { - display :block; + display: block; } } diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index c44f5765a..06021921c 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -55,14 +55,6 @@ const mapOptions = { }; 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); /** * Consider integrating later: allows for drawing, circling, making shapes on map @@ -89,7 +81,6 @@ 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(); @@ -111,7 +102,6 @@ export class MapBox extends ViewBoxAnnotatableComponent(); @observable private searchMarkers: google.maps.Marker[] = []; @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); @@ -139,11 +129,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { - this.searchBox = searchBox; - }; - // iterate allMarkers to size, center, and zoom map to contain all markers private fitBounds = (map: google.maps.Map) => { const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds(); @@ -503,7 +488,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { this._marqueeing = undefined; - this._isAnnotating = false; x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false); }; @@ -813,33 +797,32 @@ export class MapBox extends ViewBoxAnnotatableComponent (this.bingSearchBarContents = newText)} placeholder="..." size="medium" text="Boston, MA" onKeyPress={(e: { key: any }) => console.log(e.key)} />} - - {!MapBox.UseBing ? null : } - - {!MapBox.UseBing ? null : this.placePinOn ? : } - - {!MapBox.UseBing ? null : ( - - {this.allMapPushpins - .filter(marker => marker.infoWindowOpen) - .map(marker => ( - // console.log('this is a marker window') - - ))} - - )} + ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="..." text="Boston, MA" /> + + + + {this.placePinOn ? : } + + + {this.allMapPushpins + .filter(marker => marker.infoWindowOpen) + .map(marker => ( + // console.log('this is a marker window') + + ))} + + {/* {/* */} -
+
const apiKey = process.env.GOOGLE_MAPS; const script = document.createElement('script'); @@ -88,15 +85,14 @@ const options = { } as google.maps.places.AutocompleteOptions; @observer -export class MapBox2 extends ViewBoxAnnotatableComponent>() { - static UseBing = true; +export class MapBox extends ViewBoxAnnotatableComponent>() { private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [name: string]: IReactionDisposer } = {}; private _annotationLayer: React.RefObject = React.createRef(); @observable private _overlayAnnoInfo: Opt; showInfo = action((anno: Opt) => (this._overlayAnnoInfo = anno)); public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(MapBox2, fieldKey); + return FieldView.LayoutString(MapBox, fieldKey); } public get SidebarKey() { return this.fieldKey + '-sidebar'; @@ -557,113 +553,9 @@ export class MapBox2 extends ViewBoxAnnotatableComponent 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(), - }); - } - }); - }; - - - - - bingSearchBarContents: any = "Boston, MA"; // For Bing Maps: The contents of the Bing search bar (string) - - /* - * For Bing Maps - * Called by search button's onClick - * Finds the geocode of the searched contents and sets location to that location - **/ - @action - bingSearch = async() =>{ - - // clear all pins - this._bingMap.current.entities.clear(); - - const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); - - // this.rootDoc.latitude =location.latitude; - // this.rootDoc.longitude =location.longitude; // TODO: How to update the rootDoc with the correct info? - //DocComponents file is where rootDoc is - - // call a helper method that updates the this._bingMap.current.setView, - // replaces this method call below - this._bingMap.current.setView({ - center: new this.MicrosoftMaps.Location(location.latitude, location.longitude), - // zoom: , - }); - - //Create custom Pushpin - var pin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { - title: this.bingSearchBarContents, - subTitle: 'subtitle here', - text: '1' - }); - - //Add the pushpin to the map - this._bingMap.current.entities.push(pin); - // const mapMarker = Docs.Create.MapMarkerDocument(this._bingMap.location.latitude, this._bingMap.location.latitude, false, [], {}); - // this.addDocument(mapMarker, this.annotationKey); // tells mapbox to add this marker to set of annotations on doc - } - - - // /** - // * For Bing Maps - // * Place the marker on bing maps & store the empty marker as a MapMarker Document in allMarkers list - // * @param position - the LatLng position where the marker is placed - // * @param map - // */ - // @action - // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => { - // const marker = new google.maps.Marker({ - // position: position, - // map: map, - // }); - // map.panTo(position); - // const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); - // this.addDocument(mapMarker, this.annotationKey); - // }; - - - - - - bingViewOptions = { - center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },// TODO: latitude: this.rootDoc.latitude, longitude: this.rootDoc.longitude - mapTypeId: 'grayscale', - }; - bingMapOptions = { - navigationBarMode: 'square', - }; - bingMapReady = (map: any) => (this._bingMap = map.map); render() { const renderAnnotations = (docFilters?: () => string[]) => null; return ( @@ -673,57 +565,14 @@ export class MapBox2 extends ViewBoxAnnotatableComponent 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})` }}> - - + style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
{renderAnnotations(this.transparentFilter)}
{renderAnnotations(this.opaqueFilter)} {SnappingManager.GetIsDragging() ? null : renderAnnotations()} {this.annotationLayer} - - - {!MapBox2.UseBing ? null : - this.bingSearchBarContents = newText} - placeholder="..." - size="medium" - text="Boston, MA" - onKeyPress={e => console.log(e.key)} - />} - - {!MapBox2.UseBing ? null : - } - - - {!MapBox2.UseBing ? null : } - {this.allMapMarkers - .filter(marker => marker.infoWindowOpen) - .map(marker => ( - - ))} - - -
+
e.stopPropagation()} placeholder="Enter location" /> @@ -735,7 +584,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent (
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( {/* */} -
+
Date: Thu, 15 Jun 2023 13:33:30 -0400 Subject: added pushpin selected icon chagne --- src/client/views/SidebarAnnos.tsx | 3 +++ src/client/views/nodes/MapBox/MapBox.tsx | 43 +++++++++++++++++++------------- 2 files changed, 29 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 741e87644..42993bfc4 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -219,7 +219,10 @@ export class SidebarAnnos extends React.Component { {Array.from(this.allMetadata.keys()) .sort() .map(key => renderMeta(key, this.allMetadata.get(key)))} + Hello
+ +
(); @observable private searchMarkers: google.maps.Marker[] = []; - @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); + // @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); @observable private _savedAnnotations = new ObservableMap(); @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); @@ -607,17 +607,20 @@ export class MapBox extends ViewBoxAnnotatableComponent any } }) => { + pushpinClicked = (e: { + isSelected: boolean; target: { metadata: { title: any; description: any }; getLocation: () => any } +}, pin:Doc) => { + // pin.isSelected = true; //Make sure the infobox has metadata to display. - if (e.target.metadata) { - //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e? - this.infobox.setOptions({ - location: e.target.getLocation(), - title: e.target.metadata.title, - description: e.target.metadata.description, - visible: true, - }); - } + // if (e.target.metadata) { + // //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e? + // this.infobox.setOptions({ + // location: e.target.getLocation(), + // title: e.target.metadata.title, + // description: e.target.metadata.description, + // visible: true, + // }); + // } }; //PushpinClicked using MapBoxInfoWindow @@ -643,7 +646,6 @@ export class MapBox extends ViewBoxAnnotatableComponent this.createInfobox(pin)); this.allMapPushpins.map(pin => this._bingMap.current.entities.push(pin)); + + var numSelected = 0 + this.allMapPushpins.filter(pin => pin.isSelected).forEach(pin=>numSelected++) + console.log(numSelected) + }; @action createInfobox = (pin: any) => { + var pushPin2 = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}); var pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { - // title: this.bingSearchBarContents, - // subTitle: 'subtitle here', - // text: '1' + title: this.bingSearchBarContents, + subTitle: 'subtitle here', + text: '1', + // height: '50px' }); - this._bingMap.current.entities.push(pushPin); + this._bingMap.current.entities.push(pushPin2); var id = Utils.GenerateGuid(); document.getElementById(id); @@ -740,7 +749,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(e)); + this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(e, pushPin)); // this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e)); }; -- cgit v1.2.3-70-g09d2 From 6059af1b5ebe7bd011635a56ea8f30519eb6037f Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 15 Jun 2023 14:03:59 -0400 Subject: day: --- src/client/views/nodes/MapBox/MapBox.tsx | 34 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index fdd703604..ee7f89ac2 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -2,6 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; import { EditableText } from 'browndash-components'; +import e from 'connect-flash'; import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -610,7 +611,8 @@ export class MapBox extends ViewBoxAnnotatableComponent any } }, pin:Doc) => { - // pin.isSelected = true; + pin["infoWindowOpen"] = !pin.infoWindowOpen; + console.log("Pin clicked") //Make sure the infobox has metadata to display. // if (e.target.metadata) { // //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e? @@ -627,7 +629,7 @@ export class MapBox extends ViewBoxAnnotatableComponent any } }, pin: Doc) => { // // set which place was clicked // this.selectedPlace = place; - pin.infoWindowOpen = true; + pin.infoWindowOpen = !pin.infoWindowOpen; // console.log("later:" + pin.infoWindowOpen) }; @@ -696,22 +698,28 @@ export class MapBox extends ViewBoxAnnotatableComponent this.createInfobox(pin)); this.allMapPushpins.map(pin => this._bingMap.current.entities.push(pin)); - var numSelected = 0 - this.allMapPushpins.filter(pin => pin.isSelected).forEach(pin=>numSelected++) - console.log(numSelected) + }; @action createInfobox = (pin: any) => { - var pushPin2 = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}); - var pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { - title: this.bingSearchBarContents, - subTitle: 'subtitle here', - text: '1', - // height: '50px' - }); - this._bingMap.current.entities.push(pushPin2); + var pushPin:any; + if (pin.infoWindowOpen){ + pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { + title: this.bingSearchBarContents, + subTitle: 'subtitle here', + text: '1', + // height: '50px' + }); + } + else{ + pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}); + } + + this._bingMap.current.entities.push(pushPin); + + console.log(this.allMapPushpins.filter(pin => pin.infoWindowOpen).length) var id = Utils.GenerateGuid(); document.getElementById(id); -- cgit v1.2.3-70-g09d2 From 1e41c94b0003822b102f0251f98676d80eb4b56b Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 20 Jun 2023 11:41:22 -0400 Subject: Basic selection --- src/client/views/nodes/MapBox/MapBox.tsx | 54 +++++++++++++------------------- 1 file changed, 21 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index ee7f89ac2..7d3dcb811 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -3,6 +3,7 @@ import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; import { EditableText } from 'browndash-components'; import e from 'connect-flash'; +import { truncateSync } from 'fs'; import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -607,30 +608,21 @@ export class MapBox extends ViewBoxAnnotatableComponent any } -}, pin:Doc) => { - pin["infoWindowOpen"] = !pin.infoWindowOpen; - console.log("Pin clicked") - //Make sure the infobox has metadata to display. - // if (e.target.metadata) { - // //Set the infobox options with the metadata of the pushpin. // HOW DO I GET THE CORRECT INFOBOX FOR THIS PIN? CAN I use this e? - // this.infobox.setOptions({ - // location: e.target.getLocation(), - // title: e.target.metadata.title, - // description: e.target.metadata.description, - // visible: true, - // }); + pushpinClicked = (pin:Doc) => { + pin["infoWindowOpen"] = + // true + !pin.infoWindowOpen; + // if (this.selectedPin){ + // this.selectedPin.infoWindowOpen = false; + // this.selectedPin = pin // } - }; - - //PushpinClicked using MapBoxInfoWindow - private pushpinClicked2 = (e: { target: { metadata: { title: any; description: any }; getLocation: () => any } }, pin: Doc) => { - // // set which place was clicked - // this.selectedPlace = place; - pin.infoWindowOpen = !pin.infoWindowOpen; - // console.log("later:" + pin.infoWindowOpen) + // else{ + // this.selectedPin = pin + // } + this.addAllPins(); }; /** @@ -693,23 +685,21 @@ export class MapBox extends ViewBoxAnnotatableComponent { this._bingMap.current.entities.clear(); - //TODO: clear all infoboxes if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin); - this.allMapPushpins.map(pin => this.createInfobox(pin)); - this.allMapPushpins.map(pin => this._bingMap.current.entities.push(pin)); - + this.allMapPushpins.map(pin => this.createPushPin(pin)); + }; @action - createInfobox = (pin: any) => { + createPushPin = (pin: any) => { var pushPin:any; if (pin.infoWindowOpen){ pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { - title: this.bingSearchBarContents, - subTitle: 'subtitle here', - text: '1', + // title: this.bingSearchBarContents, + // subTitle: 'subtitle here', + // text: '1', // height: '50px' }); } @@ -719,8 +709,6 @@ export class MapBox extends ViewBoxAnnotatableComponent pin.infoWindowOpen).length) - var id = Utils.GenerateGuid(); document.getElementById(id); var infoboxTemplate = '
{title}
{description}
'; @@ -757,7 +745,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(e, pushPin)); + this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin)); // this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e)); }; -- cgit v1.2.3-70-g09d2 From a6878385b554c976f73fd14cfa29f6417cf4ee0b Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 26 Jun 2023 11:32:33 -0400 Subject: Pre-new architecture --- src/client/views/nodes/MapBox/MapMarkerBox.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/client/views/nodes/MapBox/MapMarkerBox.tsx (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapMarkerBox.tsx b/src/client/views/nodes/MapBox/MapMarkerBox.tsx new file mode 100644 index 000000000..e69de29bb -- cgit v1.2.3-70-g09d2 From 9d48c95e5a556f5be4abde83d9443e384a33197c Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 10 Jul 2023 14:26:37 -0400 Subject: Location metadata synced and reactions working --- src/client/documents/DocumentTypes.ts | 2 + src/client/documents/Documents.ts | 22 +- src/client/views/nodes/DocumentContentsView.tsx | 2 + src/client/views/nodes/MapBox/MapBox.tsx | 264 +++++++++++++----------- src/client/views/nodes/MapBox/MapBox2.tsx | 4 +- src/client/views/nodes/MapBox/MapMarkerBox.tsx | 0 src/client/views/nodes/MapBox/MapPushpinBox.tsx | 38 ++++ src/client/views/nodes/trails/PresBox.tsx | 19 +- src/fields/Doc.ts | 7 + 9 files changed, 235 insertions(+), 123 deletions(-) delete mode 100644 src/client/views/nodes/MapBox/MapMarkerBox.tsx create mode 100644 src/client/views/nodes/MapBox/MapPushpinBox.tsx (limited to 'src') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 2da3a24fd..2cfd9e680 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -27,6 +27,7 @@ export enum DocumentType { MAP = 'map', DATAVIZ = 'dataviz', LOADING = 'loading', + // special purpose wrappers that either take no data or are compositions of lower level types LINK = 'link', @@ -40,6 +41,7 @@ export enum DocumentType { SEARCHITEM = 'searchitem', COMPARISON = 'comparison', GROUP = 'group', + PUSHPIN = "pushpin", LINKDB = 'linkdb', // database of links ??? why do we have this SCRIPTDB = 'scriptdb', // database of scripts diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 849a5d6ae..5cced6d24 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -48,6 +48,7 @@ import { LinkBox } from '../views/nodes/LinkBox'; import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; import { LoadingBox } from '../views/nodes/LoadingBox'; import { MapBox } from '../views/nodes/MapBox/MapBox'; +import { MapPushpinBox } from '../views/nodes/MapBox/MapPushpinBox'; import { PDFBox } from '../views/nodes/PDFBox'; import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox'; import { ScreenshotBox } from '../views/nodes/ScreenshotBox'; @@ -218,8 +219,8 @@ export class DocumentOptions { dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true); openFactoryLocation?: string; // an OpenWhere value to place the factory created document openFactoryAsDelegate?: boolean; // - lat?: number; - lng?: number; + lat?: NUMt = new NumInfo('latitude of a mapping view'); + lng?: NUMt = new NumInfo('longitude of a mapping view'); infoWindowOpen?: boolean; author?: string; _layout_fieldKey?: string; @@ -286,6 +287,8 @@ export class DocumentOptions { viewTransitionTime?: number; // transition duration for view parameters presPanX?: number; // panX saved as a view spec presPanY?: number; // panY saved as a view spec + presLat?: NUMt = new NumInfo('latitude of a map'); // latitude of a map + presLong?: NUMt = new NumInfo('longitude of map'); // longitude of map presViewScale?: number; // viewScale saved as a view Spec presTransition?: number; //the time taken for the transition TO a document presDuration?: number; //the duration of the slide in presentation view @@ -683,6 +686,13 @@ export namespace Docs { options: { _layout_fitWidth: true, _fitHeight: true, nativeDimModifiable: true }, }, ], + [ + DocumentType.PUSHPIN, + { + layout: { view: MapPushpinBox, dataField: defaultDataKey }, + options: {}, + }, + ], ]); const suffix = 'Proto'; @@ -1028,8 +1038,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.MAP), new List(documents), options); } - export function MapMarkerDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), { lat, lng, infoWindowOpen, ...options }, id); + export function PushpinDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array, options: DocumentOptions, id?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { lat, lng, 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) @@ -1058,6 +1068,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id); } + export function MapanchorDocument(options: DocumentOptions = {}, id?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id); + } + export function InkAnchorDocument(options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id); } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 348ef910a..9546a6d38 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -44,6 +44,7 @@ import { VideoBox } from './VideoBox'; import { WebBox } from './WebBox'; import React = require('react'); import XRegExp = require('xregexp'); +import { MapPushpinBox } from './MapBox/MapPushpinBox'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -266,6 +267,7 @@ export class DocumentContentsView extends React.Component< ComparisonBox, LoadingBox, SchemaRowBox, + MapPushpinBox, }} bindings={bindings} jsx={layoutFrame} diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 7d3dcb811..f1f6b0756 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -4,17 +4,19 @@ import BingMapsReact from 'bingmaps-react'; import { EditableText } from 'browndash-components'; import e from 'connect-flash'; import { truncateSync } from 'fs'; -import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; +import { ScriptField } from '../../../../fields/ScriptField'; import { NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; +import { Transform } from '../../../util/Transform'; import { UndoManager } from '../../../util/UndoManager'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; @@ -23,8 +25,9 @@ import { MarqueeAnnotator } from '../../MarqueeAnnotator'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { Annotation } from '../../pdf/Annotation'; import { SidebarAnnos } from '../../SidebarAnnos'; +import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; -import { PinProps } from '../trails'; +import { PinProps, PresBox } from '../trails'; import './MapBox.scss'; import { MapBoxInfoWindow } from './MapBoxInfoWindow'; // amongus @@ -126,11 +129,23 @@ export class MapBox extends ViewBoxAnnotatableComponent(); private _ref: React.RefObject = React.createRef(); - + private _disposer: {[key:string]:IReactionDisposer} = {} componentDidMount() { this.props.setContentView?.(this); + this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude}), + (locationObject) => { + + //TODO: SAVE ZOOM? VIEW STYLE? + this._bingMap.current.setView({ + center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng), + }); + + }, {fireImmediately: true}); } + componentWillUnmount(): void { + Object.keys(this._disposer).forEach(key => this._disposer[key]?.()) + } // iterate allMarkers to size, center, and zoom map to contain all markers private fitBounds = (map: google.maps.Map) => { const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds(); @@ -240,7 +255,7 @@ export class MapBox extends ViewBoxAnnotatableComponent AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; + // Old get anchor function + // getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; /** * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker @@ -592,57 +608,78 @@ export class MapBox extends ViewBoxAnnotatableComponent { - this._bingMap.current.setView({ - center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude), - // zoom: location - }); - }; + + /* + * Creates Pushpin doc and adds it to the list of annotations + */ @action createPushpin = (latitude: number, longitude: number) => { // Stores the pushpin as a MapMarkerDocument - const mapMarker = Docs.Create.MapMarkerDocument(NumCast(latitude), NumCast(longitude), false, [], {}); + const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {}); this.addDocument(mapMarker, this.annotationKey); - mapMarker.infoWindowOpen = true; - }; + // mapMarker.infoWindowOpen = true; + }; - selectedPin: Doc | undefined; - infobox: any; + /* + * Pushpin onclick + */ pushpinClicked = (pin:Doc) => { - pin["infoWindowOpen"] = - // true - !pin.infoWindowOpen; - // if (this.selectedPin){ - // this.selectedPin.infoWindowOpen = false; - // this.selectedPin = pin - // } - // else{ - // this.selectedPin = pin - // } - this.addAllPins(); + pin.infoWindowOpen = !pin.infoWindowOpen; + + // @action + // onPointerDown = (e: React.PointerEvent) => { + // if (e.button === 2 || e.ctrlKey) { + // AnchorMenu.Instance.Status = 'annotation'; + // AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this); + // AnchorMenu.Instance.Pinned = false; + // AnchorMenu.Instance.PinToPres = this.pinToPres; + // AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle; + // AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler; + // AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion); + // AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true); + // e.stopPropagation(); + // } else if (e.button === 0) { + // e.stopPropagation(); + // LinkFollower.FollowLink(undefined, this.annoTextRegion, false); + // } + // }; + + // TODO: UPDATE FOR DASHDOC SELECTION }; /** - * Returns a list of MapMarkerDocument + * Returns a list of Pushpin docs */ @computed get allMapPushpins() { return DocListCast(this.dataDoc[this.annotationKey]); } + /** - * Map OnClick create pushpin + * Map OnClick ~> creates a pushpin */ @action mapOnClick = (e: { location: { latitude: any; longitude: any } }) => { if (this.placePinOn) { this.createPushpin(e.location.latitude, e.location.longitude); - this.addAllPins(); + // this.addAllPins(); this.placePinOn = false; } }; + + + + /* + * Updates values of layout doc to match the current map + */ + @action + updateLayout = () => { + this.dataDoc.latitude = this._bingMap.current.getCenter().latitude; + this.dataDoc.longitude = this._bingMap.current.getCenter().longitude; + }; + searched_pin: any; /* * For Bing Maps @@ -651,20 +688,20 @@ export class MapBox extends ViewBoxAnnotatableComponent { - this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick); - this.addAllPins(); - const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); - this.dataDoc.latitude = location.latitude; this.dataDoc.longitude = location.longitude; - this.updateView(); + // Centers on the searched location + this._bingMap.current.setView({ + center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude), + }); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', this.updateLayout); + + // Creates a temporary pin but does not add it to the dataDoc var temp = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { - // title: this.bingSearchBarContents, - // subTitle: 'subtitle here', - // text: '1', color: 'blue', }); if (temp != this.searched_pin || this.searched_pin == null) { @@ -672,82 +709,65 @@ export class MapBox extends ViewBoxAnnotatableComponent marker.infoWindowOpen) - // .length) - // console.log(this.allMapPushpins - // .map(marker => console.log(marker.infoWindowOpen))) }; /** - * Adds all pushpins in dataDoc onto the map + * Adds all pushpins in dataDoc onto the map (render) - OLD & UNUSED */ @action addAllPins = () => { this._bingMap.current.entities.clear(); if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin); - - this.allMapPushpins.map(pin => this.createPushPin(pin)); - - + // this.allMapPushpins.map(pin => this.addPushpin(pin)); }; - @action - createPushPin = (pin: any) => { - var pushPin:any; - if (pin.infoWindowOpen){ - pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { - // title: this.bingSearchBarContents, - // subTitle: 'subtitle here', - // text: '1', - // height: '50px' + /* + * Returns doc w/ relevant info + */ + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { + const anchor = + Docs.Create.MapanchorDocument({ + title: 'MapAnchor:' + this.rootDoc.title, + presLat: NumCast(this.dataDoc.latitude), + presLong: NumCast(this.dataDoc.longitude), + // presTransition: 1000, + unrendered: true, + annotationOn: this.rootDoc, }); + if (anchor) { + if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; + /* addAsAnnotation &&*/ this.addDocument(anchor); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc); + return anchor; } - else{ - pushPin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}); - } - - this._bingMap.current.entities.push(pushPin); - - var id = Utils.GenerateGuid(); - document.getElementById(id); - var infoboxTemplate = '
{title}
{description}
'; - // var infowindowtemp = '' - - // console.log(
) - // var htmlString = ReactDOMServer.renderToString(
); - - //Create an infobox at the pin - this.infobox = new this.MicrosoftMaps.Infobox(new this.MicrosoftMaps.Location(pin.lat, pin.lng), { - htmlContent: infoboxTemplate, // style="width:100; height:100; background:blue" - visible: false, - }); - try { - // console.log(document.getElementById(id)) - // console.log(this.infobox) - // const root = ReactDOM.createRoot(document.getElementById(id)!); - // root.render(
); - } catch (e) { - console.log(e); - } + return this.rootDoc; + }; - //Assign the infobox to a map instance. - this.infobox.setMap(this._bingMap.current); - //Store some metadata with the pushpin. - pushPin.metadata = { - title: pushPin.title, - description: 'Pin description', - }; - // pushPin.infoWindowOpen = true; + /* + * Input: pin doc + * Adds MicrosoftMaps Pushpin to the map (render) + */ + @action + addPushpin = (pin: Doc) => { + const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}); - //Add a click event handler to the pushpin. - // For bing maps infobox - // this.MicrosoftMaps.Events.addHandler(pushPin, 'click', this.pushpinClicked); + + this._bingMap.current.entities.push(pushPin); // For our infowindow this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin)); - // this.MicrosoftMaps.Events.addHandler(pushPin, 'mouseover', (e:any) => this.onHover(e)); - }; + } + + /* + * Input: pin doc + * Adds MicrosoftMaps Pushpin to the map (render) + */ + @action + removePushpin = (pin:any)=>{ + this._bingMap.current.entities.clear(); + this.allMapPushpins + this.allMapPushpins.map(pin => this.addPushpin(pin)); + } /** * View options for bing maps @@ -809,25 +829,37 @@ export class MapBox extends ViewBoxAnnotatableComponent : } + +
{this.allMapPushpins - .filter(marker => marker.infoWindowOpen) - .map(marker => ( - // console.log('this is a marker window') - ( + + + // HOW DO I PASS IN pushpin??? + // HOW DO I MAKE SURE IT KNOWS TO MAKE MapPushpinBox??? + Document={pushpin} + DataDoc={undefined} + PanelWidth={returnOne} + PanelHeight={returnOne} + NativeWidth={returnOne} + NativeHeight={returnOne} + onClick={()=>new ScriptField(undefined)} + onKey={undefined} + onDoubleClick={undefined} + onBrowseClick={undefined} + docFilters={returnEmptyDoclist} + docRangeFilters={returnEmptyDoclist} + searchFilterDocs={returnEmptyDoclist} + isDocumentActive={returnFalse} + isContentActive={returnFalse} + addDocTab={returnFalse} + ScreenToLocalTransform={()=>new Transform(0,0,0)} + fitContentsToBox={undefined} + focus={returnOne} + /> ))} - - +
{/* () { + + + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(MapPushpinBox, fieldKey); + } + componentDidMount() { + this.mapBoxView.addPushpin(this.rootDoc); + } + componentWillUnmount() { + this.mapBoxView.removePushpin(this.rootDoc); + } + + + @computed get mapBoxView() { + return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as MapBox; + } + @computed get mapBox() { + return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc; + } + + render() { + return (
); + } +} diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 858d83b7a..d1cfb86ae 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -41,6 +41,7 @@ export interface pinDataTypes { scrollable?: boolean; dataviz?: number[]; pannable?: boolean; + map?:boolean; viewType?: boolean; inkable?: boolean; filters?: boolean; @@ -378,6 +379,7 @@ export class PresBox extends ViewBoxBaseComponent() { const inkable = [DocumentType.INK].includes(targetType); const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._viewType === CollectionViewType.Stacking; const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform); + const map = [DocumentType.MAP].includes(targetType); const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType); const clippable = [DocumentType.COMPARISON].includes(targetType); const datarange = [DocumentType.FUNCPLOT].includes(targetType); @@ -387,7 +389,7 @@ export class PresBox extends ViewBoxBaseComponent() { const filters = true; const pivot = true; const dataannos = false; - return { scrollable, pannable, inkable, viewType, pivot, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos }; + return { scrollable, pannable, inkable, viewType, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos }; } @action @@ -455,6 +457,17 @@ export class PresBox extends ViewBoxBaseComponent() { changed = true; } } + if (pinDataTypes?.map || (!pinDataTypes && activeItem.presLat !== undefined)) { + if (bestTarget.latitude !== activeItem.presLat) { + Doc.SetInPlace(bestTarget, "latitude", NumCast(activeItem.presLat), true); + changed = true; + } + if (bestTarget.longitude !== activeItem.presLong) { + Doc.SetInPlace(bestTarget, "longitude", NumCast(activeItem.presLong), true); + bestTarget.restoreTargetOn = true; + changed = true; + } + } if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) { if (bestTarget._layout_currentTimecode !== activeItem.presStartTime) { bestTarget._layout_currentTimecode = activeItem.presStartTime; @@ -633,6 +646,10 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presXRange = undefined; //targetDoc?.xrange; pinDoc.presYRange = undefined; //targetDoc?.yrange; } + if (pinProps.pinData.map) { + pinDoc.presLat = targetDoc?.lat; + //... + } if (pinProps.pinData.poslayoutview) pinDoc.presPinLayoutData = new List( DocListCast(targetDoc[fkey] as ObjectField).map(d => diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index deda4c876..0cf4256d6 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -522,6 +522,13 @@ export namespace Doc { export function IsDelegateField(doc: Doc, fieldKey: string) { return doc && Get(doc, fieldKey, true) !== undefined; } + // + // this will write the value to the key on either the data doc or the embedding doc. The choice + // of where to write it is based on: + // 1) if the embedding Doc already has this field defined on it, then it will be written to the embedding + // 2) if the data doc has the field, then it's written there. + // 3) if neither already has the field, then 'defaultProto' determines whether to write it to the data doc (or the embedding) + // export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) { if (key.startsWith('_')) key = key.substring(1); const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined; -- cgit v1.2.3-70-g09d2 From b04306ce09bfd994c0535d77bae9c778dab97977 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 12 Jul 2023 11:40:44 -0400 Subject: Pre-zoom saving to doc --- src/client/views/nodes/MapBox/MapBox.tsx | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index f1f6b0756..3c8e6203a 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -835,9 +835,6 @@ export class MapBox extends ViewBoxAnnotatableComponent ( Date: Tue, 18 Jul 2023 16:06:29 -0400 Subject: continued fixing merge --- src/client/views/FilterPanel.scss | 13 ++++++++----- src/client/views/FilterPanel.tsx | 2 +- src/client/views/PropertiesView.scss | 11 +++++++++++ src/client/views/PropertiesView.tsx | 5 +++-- 4 files changed, 23 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss index 4f0460659..bb68afed3 100644 --- a/src/client/views/FilterPanel.scss +++ b/src/client/views/FilterPanel.scss @@ -135,7 +135,7 @@ .filterBox-addFilter { width: 120px; - background-color: #e9e9e9; + backgroundcolor: #e9e9e9; border-radius: 12px; padding: 5px; margin: 5px; @@ -199,11 +199,14 @@ // float:right; .filterBox-facetHeader-collapse{ - float: right; - justify-items: right; - align-items: flex-end; + // float: right; + // justify-items: right; + // align-items: flex-end; margin-left: auto; - margin-right: 9px; + // margin-right: 9px; + + float: right; + font-size: 16; } } diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 68d29942b..5d1e5c08d 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -198,7 +198,7 @@ export class FilterPanel extends React.Component {
- {/* */} +
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 060b506e3..23bf31b95 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -26,6 +26,17 @@ padding: 5px 10px; } + .propertiesView-propAndInfoGrouping{ + display: flex; + } + + .propertiesView-info{ + margin-top: 20; + margin-right: 10; + float: right; + font-size: 20; + } + .propertiesView-sharing { //border-bottom: 1px solid black; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index cb0663554..045d70591 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -584,7 +584,8 @@ export class PropertiesView extends React.Component { return (
-
Type
+ Type + {/*
Type
*/}
{this.currentComponent} @@ -1734,7 +1735,7 @@ export class PropertiesView extends React.Component {
Properties
-
window.open('https://brown-dash.github.io/Dash-Documentation/')}> +
window.open('https://brown-dash.github.io/Dash-Documentation//properties/')}>
-- cgit v1.2.3-70-g09d2 From a193bdcb4e3abbb41698ef1ed0f65ff8407c5bf4 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Wed, 19 Jul 2023 13:23:25 -0400 Subject: collapsing in filters works --- src/client/views/FilterPanel.tsx | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 5d1e5c08d..1ab7ab54e 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -17,6 +17,12 @@ import { CiCircleRemove } from 'react-icons/ci'; interface filterProps { rootDoc: Doc; } + +const handleCollapse = (currentHeader: string) => { + // this._chosenFacetsCollapse.set(currentHeader, true); + console.log("hi") +} + @observer export class FilterPanel extends React.Component { public static LayoutString(fieldKey: string) { @@ -108,12 +114,16 @@ export class FilterPanel extends React.Component { }; @observable _chosenFacets = new ObservableMap(); + @observable _chosenFacetsCollapse = new ObservableMap(); + @computed get activeFacets() { + console.log("chosen collpase " + this._chosenFacetsCollapse) const facets = new Map(this._chosenFacets); StrListCast(this.targetDoc?._childFilters).map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')); setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')))); return facets; } + /** * Responds to clicking the check box in the flyout menu */ @@ -141,6 +151,7 @@ export class FilterPanel extends React.Component { } else { this._chosenFacets.set(facetHeader, 'checkbox'); } + this._chosenFacetsCollapse.set(facetHeader, false) }; facetValues = (facetHeader: string) => { @@ -167,10 +178,11 @@ export class FilterPanel extends React.Component { return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2)); }; + // need boolean that is true or false on click but individual to that specific header + render() { const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); - console.log("this is option " + options) - console.log("this is alll facets " + this._allFacets) + return (
@@ -197,14 +209,18 @@ export class FilterPanel extends React.Component { {facetHeader.charAt(0).toUpperCase() + facetHeader.slice(1)}
- +
{ + const collapseBoolValue = this._chosenFacetsCollapse.get(facetHeader) + this._chosenFacetsCollapse.set(facetHeader, !collapseBoolValue )})}> +
+
- - {this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader)} + { this._chosenFacetsCollapse.get(facetHeader) ? null : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } + {/* */}
))}
@@ -213,6 +229,7 @@ export class FilterPanel extends React.Component { } private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string): React.ReactNode { + console.log("opening a specific " + this.activeFacets.get(facetHeader)) switch (type) { case 'text': return ( -- cgit v1.2.3-70-g09d2 From e29a26a5095bbf8f05d327b1bbefc991967c6f2b Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Wed, 19 Jul 2023 15:58:19 -0400 Subject: filter - trying to work on removing --- src/client/views/FilterPanel.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 1ab7ab54e..12dbf691f 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -18,11 +18,6 @@ interface filterProps { rootDoc: Doc; } -const handleCollapse = (currentHeader: string) => { - // this._chosenFacetsCollapse.set(currentHeader, true); - console.log("hi") -} - @observer export class FilterPanel extends React.Component { public static LayoutString(fieldKey: string) { @@ -178,7 +173,7 @@ export class FilterPanel extends React.Component { return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2)); }; - // need boolean that is true or false on click but individual to that specific header + // change css and make the currently selected filters appear at the top render() { const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); @@ -214,7 +209,10 @@ export class FilterPanel extends React.Component { this._chosenFacetsCollapse.set(facetHeader, !collapseBoolValue )})}>
- +
this.activeFacets.delete(facetHeader)) + } > +
-- cgit v1.2.3-70-g09d2 From 054ca2c87ebd67f24ddf70e686f0188d6a4fa620 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Thu, 20 Jul 2023 11:58:23 -0400 Subject: filter - closing works w/o filters selected --- src/client/views/FilterPanel.tsx | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 12dbf691f..416843f14 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -209,9 +209,18 @@ export class FilterPanel extends React.Component { this._chosenFacetsCollapse.set(facetHeader, !collapseBoolValue )})}>
-
this.activeFacets.delete(facetHeader)) - } > +
{ + + // delete this.activeFacets[facetHeader]; + this.activeFacets.delete(facetHeader) + this._chosenFacets.delete(facetHeader) + //console.log("TRYING SOMETHING NEW " + e.target.checked) + + } + ) + } + // onChange = {undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, 'check'), 'set filter')} + >
@@ -227,7 +236,7 @@ export class FilterPanel extends React.Component { } private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string): React.ReactNode { - console.log("opening a specific " + this.activeFacets.get(facetHeader)) + console.log("im here") switch (type) { case 'text': return ( @@ -242,6 +251,7 @@ export class FilterPanel extends React.Component { /> ); case 'checkbox': + // console.log("checking") return this.facetValues(facetHeader).map(fval => { const facetValue = fval; return ( @@ -255,6 +265,9 @@ export class FilterPanel extends React.Component { } type={type} onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} + onClick={undoable (e => + console.log("boolean value with " + facetValue + ":" + e.target.checked), 'set filter' + ) } /> {facetValue}
-- cgit v1.2.3-70-g09d2 From f847b894e79a0914ea2864f07ece92ac90eab1de Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Thu, 20 Jul 2023 13:10:01 -0400 Subject: filter - still working on closing --- src/client/views/FilterPanel.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 416843f14..da00ae255 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -110,6 +110,7 @@ export class FilterPanel extends React.Component { @observable _chosenFacets = new ObservableMap(); @observable _chosenFacetsCollapse = new ObservableMap(); + // @observable _currentFilters: string[] ; -- instatitae array to store the currently selected filters (Ex: #food ). remove these filters when removing @computed get activeFacets() { console.log("chosen collpase " + this._chosenFacetsCollapse) @@ -214,7 +215,9 @@ export class FilterPanel extends React.Component { // delete this.activeFacets[facetHeader]; this.activeFacets.delete(facetHeader) this._chosenFacets.delete(facetHeader) - //console.log("TRYING SOMETHING NEW " + e.target.checked) + // console.log("TRYING SOMETHING NEW " + e.target.checked) + + // check if the current values are apart of the this.facetValues(facetHeader).map(fval ... } ) @@ -236,7 +239,6 @@ export class FilterPanel extends React.Component { } private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string): React.ReactNode { - console.log("im here") switch (type) { case 'text': return ( @@ -252,6 +254,7 @@ export class FilterPanel extends React.Component { ); case 'checkbox': // console.log("checking") + return this.facetValues(facetHeader).map(fval => { const facetValue = fval; return ( @@ -266,7 +269,7 @@ export class FilterPanel extends React.Component { type={type} onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} onClick={undoable (e => - console.log("boolean value with " + facetValue + ":" + e.target.checked), 'set filter' + e.target.checked ? this._currentFilters.push(fval) : , 'set filter' ) } /> {facetValue} -- cgit v1.2.3-70-g09d2 From 50cb7d0981a71c2eb45fbecfc2befad94d431eaa Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Fri, 21 Jul 2023 15:07:04 -0400 Subject: filter - stuck on cancelling out need to remove filters and stop them from showing up. --- src/client/views/FilterPanel.tsx | 43 ++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index da00ae255..4f4e39218 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -110,6 +110,7 @@ export class FilterPanel extends React.Component { @observable _chosenFacets = new ObservableMap(); @observable _chosenFacetsCollapse = new ObservableMap(); + @observable _removeBoolean = false; // @observable _currentFilters: string[] ; -- instatitae array to store the currently selected filters (Ex: #food ). remove these filters when removing @computed get activeFacets() { @@ -213,12 +214,21 @@ export class FilterPanel extends React.Component {
{ // delete this.activeFacets[facetHeader]; + // StrListCast(this.targetDoc._childFilters).find(filter => filter.split(Doc.FilterSep)[2] = 'remove') + // .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue) + // ?.split(Doc.FilterSep)[2] === 'check' + // console.log("why cant i get this "+ console.log( StrListCast(this.targetDoc._childFilters) + // .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader))) this.activeFacets.delete(facetHeader) this._chosenFacets.delete(facetHeader) + this._chosenFacetsCollapse.delete(facetHeader) + // this._removeBoolean = true + // setTimeout(() => {this._removeBoolean = false}, 1000 ) // console.log("TRYING SOMETHING NEW " + e.target.checked) // check if the current values are apart of the this.facetValues(facetHeader).map(fval ... + // SET UP BOOLEAN AND IF IT IS TRUE MEANS ITS CLICKED AND THEN ALL BOXES SHOULD BE UNCHECKED } ) } @@ -261,16 +271,33 @@ export class FilterPanel extends React.Component {
filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue) - ?.split(Doc.FilterSep)[2] === 'check' + checked={ this._removeBoolean ? false : + StrListCast(this.targetDoc._childFilters) + .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue) + ?.split(Doc.FilterSep)[2] === 'check' + } type={type} - onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} - onClick={undoable (e => - e.target.checked ? this._currentFilters.push(fval) : , 'set filter' - ) } + onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} + // onClick={undoable (e => + // e.target.checked ? this._currentFilters.push(fval) : , 'set filter' + // ) } + onClick = {action((e) => { + console.log("im here") + console.log( StrListCast(this.targetDoc._childFilters) + .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue) + // ?.split(Doc.FilterSep)[2] === 'check' + ) + StrListCast(this.targetDoc._childFilters).find(filter => console.log(" rahhhhhh1 " + filter.split(Doc.FilterSep)[1] === facetValue)) + StrListCast(this.targetDoc._childFilters).find(filter => console.log(" rahhhhhh2 " + filter.split(Doc.FilterSep)[2]) ) + } + + // console.log("this is an experiment " + StrListCast(this.targetDoc._childFilters) + + ) + // undoable (e => console.log(" this is an experiment " + e.target.checked), 'set filter') + + } /> {facetValue}
-- cgit v1.2.3-70-g09d2 From b3c0db8e2d6f29b6b09474b8252760e95f80de95 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Mon, 24 Jul 2023 14:46:05 -0400 Subject: filter- fixed removable and css (collapse bugs) --- src/client/views/FilterPanel.scss | 6 ++ src/client/views/FilterPanel.tsx | 112 +++++++++++++++++++------------------- src/fields/Doc.ts | 1 + 3 files changed, 64 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss index bb68afed3..bb1e79d11 100644 --- a/src/client/views/FilterPanel.scss +++ b/src/client/views/FilterPanel.scss @@ -209,6 +209,12 @@ font-size: 16; } + .filterBox-facetHeader-remove{ + // margin-left: auto; + float: right; + font-size: 16; + } + } diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 4f4e39218..c99967ef7 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -14,6 +14,8 @@ import { undoable } from '../util/UndoManager'; import { AiOutlineMinusSquare } from 'react-icons/ai'; import { CiCircleRemove } from 'react-icons/ci'; +//slight bug when you don't click on background canvas before creating filter and the you click on the canvas + interface filterProps { rootDoc: Doc; } @@ -103,6 +105,8 @@ export class FilterPanel extends React.Component { return { strings: Array.from(valueSet.keys()), rtFields }; } + + public removeFilter = (filterName: string) => { Doc.setDocFilter(this.targetDoc, filterName, undefined, 'remove'); Doc.setDocRangeFilter(this.targetDoc, filterName, undefined); @@ -110,11 +114,10 @@ export class FilterPanel extends React.Component { @observable _chosenFacets = new ObservableMap(); @observable _chosenFacetsCollapse = new ObservableMap(); - @observable _removeBoolean = false; - // @observable _currentFilters: string[] ; -- instatitae array to store the currently selected filters (Ex: #food ). remove these filters when removing + @observable _currentActiveFilters = new ObservableMap(); @computed get activeFacets() { - console.log("chosen collpase " + this._chosenFacetsCollapse) + // console.log("chosen collpase " + this._chosenFacetsCollapse) const facets = new Map(this._chosenFacets); StrListCast(this.targetDoc?._childFilters).map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')); setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')))); @@ -151,6 +154,25 @@ export class FilterPanel extends React.Component { this._chosenFacetsCollapse.set(facetHeader, false) }; + @action + sortingCurrentFacetValues = (facetHeader:string) => { + + let returnKeys = []; + + console.log("this is current filtes " + this._currentActiveFilters) + for (var key of this.facetValues(facetHeader)){ + console.log("key : " + key ) + if (this._currentActiveFilters.get(key)){ + console.log("pushing") + returnKeys.push(key) + }} + console.log("this is return keys " + returnKeys) + return returnKeys.toString(); + } + + + + facetValues = (facetHeader: string) => { const allCollectionDocs = new Set(); SearchBox.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc)); @@ -175,7 +197,6 @@ export class FilterPanel extends React.Component { return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2)); }; - // change css and make the currently selected filters appear at the top render() { const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); @@ -205,41 +226,37 @@ export class FilterPanel extends React.Component {
{facetHeader.charAt(0).toUpperCase() + facetHeader.slice(1)} -
-
{ + {/*
*/} +
{ const collapseBoolValue = this._chosenFacetsCollapse.get(facetHeader) this._chosenFacetsCollapse.set(facetHeader, !collapseBoolValue )})}> -
+ + + +
-
{ - - // delete this.activeFacets[facetHeader]; - // StrListCast(this.targetDoc._childFilters).find(filter => filter.split(Doc.FilterSep)[2] = 'remove') - // .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue) - // ?.split(Doc.FilterSep)[2] === 'check' - // console.log("why cant i get this "+ console.log( StrListCast(this.targetDoc._childFilters) - // .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader))) - this.activeFacets.delete(facetHeader) - this._chosenFacets.delete(facetHeader) - this._chosenFacetsCollapse.delete(facetHeader) - // this._removeBoolean = true - // setTimeout(() => {this._removeBoolean = false}, 1000 ) - // console.log("TRYING SOMETHING NEW " + e.target.checked) - - // check if the current values are apart of the this.facetValues(facetHeader).map(fval ... - - // SET UP BOOLEAN AND IF IT IS TRUE MEANS ITS CLICKED AND THEN ALL BOXES SHOULD BE UNCHECKED - } - ) - } - // onChange = {undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, 'check'), 'set filter')} - > -
-
- +
{ + for (var key of this.facetValues(facetHeader)){ + if (this._currentActiveFilters.get(key)){ + Doc.setDocFilter(this.targetDoc, facetHeader, key, 'remove') + this._currentActiveFilters.delete(facetHeader) + }} + + this.activeFacets.delete(facetHeader) + this._chosenFacets.delete(facetHeader) + this._chosenFacetsCollapse.delete(facetHeader) + + })} > + +
+ {/*
*/} +
- { this._chosenFacetsCollapse.get(facetHeader) ? null : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } + { this._chosenFacetsCollapse.get(facetHeader) ? + this.sortingCurrentFacetValues(facetHeader) : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } {/* */}
))} @@ -263,7 +280,6 @@ export class FilterPanel extends React.Component { /> ); case 'checkbox': - // console.log("checking") return this.facetValues(facetHeader).map(fval => { const facetValue = fval; @@ -271,7 +287,7 @@ export class FilterPanel extends React.Component {
filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue) ?.split(Doc.FilterSep)[2] === 'check' @@ -279,25 +295,11 @@ export class FilterPanel extends React.Component { } type={type} onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} - // onClick={undoable (e => - // e.target.checked ? this._currentFilters.push(fval) : , 'set filter' - // ) } - onClick = {action((e) => { - console.log("im here") - console.log( StrListCast(this.targetDoc._childFilters) - .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue) - // ?.split(Doc.FilterSep)[2] === 'check' - ) - StrListCast(this.targetDoc._childFilters).find(filter => console.log(" rahhhhhh1 " + filter.split(Doc.FilterSep)[1] === facetValue)) - StrListCast(this.targetDoc._childFilters).find(filter => console.log(" rahhhhhh2 " + filter.split(Doc.FilterSep)[2]) ) - } - - // console.log("this is an experiment " + StrListCast(this.targetDoc._childFilters) - - ) - // undoable (e => console.log(" this is an experiment " + e.target.checked), 'set filter') - - } + onClick={undoable (e => + e.target.checked ? this._currentActiveFilters.set(fval, facetHeader) : this._currentActiveFilters.delete(facetHeader) , 'set filter' + ) } + + /> {facetValue}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 5a8a6e4b6..770a72855 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1437,6 +1437,7 @@ export namespace Doc { // 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) { + console.log("in setDocFilter: key "+ key + "value " + value) if (!container) return; const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters'; const childFilters = StrListCast(container[filterField]); -- cgit v1.2.3-70-g09d2 From 14032744483a7fc83d552ccbe263cf9ec487fa25 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Tue, 25 Jul 2023 12:30:07 -0400 Subject: filters - collapsing works (w/o ui improvement) --- src/client/views/FilterPanel.scss | 7 +++++++ src/client/views/FilterPanel.tsx | 26 +++++++++++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss index bb1e79d11..d774983a7 100644 --- a/src/client/views/FilterPanel.scss +++ b/src/client/views/FilterPanel.scss @@ -215,6 +215,13 @@ font-size: 16; } + + +} +.filterbox-collpasedAndActive{ + left:100px; + background-color: pink; + font-size: 100px; } diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index c99967ef7..4cfc02ff7 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -115,6 +115,9 @@ export class FilterPanel extends React.Component { @observable _chosenFacets = new ObservableMap(); @observable _chosenFacetsCollapse = new ObservableMap(); @observable _currentActiveFilters = new ObservableMap(); + @observable _collapseReturnKeys = new Array(); + + // let returnKeys = []; @computed get activeFacets() { // console.log("chosen collpase " + this._chosenFacetsCollapse) @@ -154,20 +157,16 @@ export class FilterPanel extends React.Component { this._chosenFacetsCollapse.set(facetHeader, false) }; + @action sortingCurrentFacetValues = (facetHeader:string) => { - - let returnKeys = []; - - console.log("this is current filtes " + this._currentActiveFilters) + for (var key of this.facetValues(facetHeader)){ console.log("key : " + key ) if (this._currentActiveFilters.get(key)){ - console.log("pushing") - returnKeys.push(key) + this._collapseReturnKeys.push(key) }} - console.log("this is return keys " + returnKeys) - return returnKeys.toString(); + return this._collapseReturnKeys.toString(); } @@ -241,7 +240,7 @@ export class FilterPanel extends React.Component { for (var key of this.facetValues(facetHeader)){ if (this._currentActiveFilters.get(key)){ Doc.setDocFilter(this.targetDoc, facetHeader, key, 'remove') - this._currentActiveFilters.delete(facetHeader) + this._currentActiveFilters.delete(key) }} this.activeFacets.delete(facetHeader) @@ -256,7 +255,9 @@ export class FilterPanel extends React.Component {
{ this._chosenFacetsCollapse.get(facetHeader) ? - this.sortingCurrentFacetValues(facetHeader) : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } +
{this.sortingCurrentFacetValues(facetHeader)}
&& this._collapseReturnKeys.splice(0) + + : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } {/* */}
))} @@ -296,7 +297,7 @@ export class FilterPanel extends React.Component { type={type} onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} onClick={undoable (e => - e.target.checked ? this._currentActiveFilters.set(fval, facetHeader) : this._currentActiveFilters.delete(facetHeader) , 'set filter' + e.target.checked ? this._currentActiveFilters.set(fval, facetHeader) : this._currentActiveFilters.delete(fval) , 'set filter' ) } @@ -308,3 +309,6 @@ export class FilterPanel extends React.Component { } } } + + +// NEED TO LEARN HOW TO RESET FILTERS WHEN WEBPAGE IS RELOADED \ No newline at end of file -- cgit v1.2.3-70-g09d2 From b84d3851d3baeeb0e2da5d4bd2fde9c911697d21 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Tue, 25 Jul 2023 15:05:46 -0400 Subject: filter - working w/ css (looking for recommendations) --- src/client/views/FilterPanel.scss | 13 ++++++++++--- src/client/views/FilterPanel.tsx | 16 +++++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss index d774983a7..7cf886e12 100644 --- a/src/client/views/FilterPanel.scss +++ b/src/client/views/FilterPanel.scss @@ -188,6 +188,8 @@ margin-bottom: 10px; margin-left: 5px; overflow: auto; + + } } @@ -218,11 +220,16 @@ } + .filterbox-collpasedAndActive{ - left:100px; - background-color: pink; - font-size: 100px; + // left:100px; + text-indent: 18px; + // background-color: pink; + font-size: 12px; + font-weight: bold; + } + diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 4cfc02ff7..b03e11f79 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -161,12 +161,21 @@ export class FilterPanel extends React.Component { @action sortingCurrentFacetValues = (facetHeader:string) => { + this._collapseReturnKeys.splice(0) + for (var key of this.facetValues(facetHeader)){ console.log("key : " + key ) if (this._currentActiveFilters.get(key)){ this._collapseReturnKeys.push(key) }} - return this._collapseReturnKeys.toString(); + // return "hello" + + return (
+ + {this._collapseReturnKeys.toString()} +
) + + } @@ -255,8 +264,9 @@ export class FilterPanel extends React.Component {
{ this._chosenFacetsCollapse.get(facetHeader) ? -
{this.sortingCurrentFacetValues(facetHeader)}
&& this._collapseReturnKeys.splice(0) - + //
{this.sortingCurrentFacetValues(facetHeader)}
&& this._collapseReturnKeys.splice(0) + this.sortingCurrentFacetValues(facetHeader) + // && this._collapseReturnKeys.splice(0) : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } {/* */}
-- cgit v1.2.3-70-g09d2 From 43670f51a7dd627deeb3612f1066e51871235f2d Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Wed, 26 Jul 2023 15:35:59 -0400 Subject: filter - UI updates --- src/client/views/FilterPanel.scss | 1 + src/client/views/FilterPanel.tsx | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss index 7cf886e12..78e7904b8 100644 --- a/src/client/views/FilterPanel.scss +++ b/src/client/views/FilterPanel.scss @@ -215,6 +215,7 @@ // margin-left: auto; float: right; font-size: 16; + font-weight:bold; } diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index b03e11f79..b14e73208 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -11,7 +11,7 @@ import './FilterPanel.scss'; import { FieldView } from './nodes/FieldView'; import { SearchBox } from './search/SearchBox'; import { undoable } from '../util/UndoManager'; -import { AiOutlineMinusSquare } from 'react-icons/ai'; +import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai'; import { CiCircleRemove } from 'react-icons/ci'; //slight bug when you don't click on background canvas before creating filter and the you click on the canvas @@ -172,7 +172,8 @@ export class FilterPanel extends React.Component { return (
- {this._collapseReturnKeys.toString()} + {this._collapseReturnKeys.join(', ') } + {/* .toString()} */}
) @@ -240,7 +241,7 @@ export class FilterPanel extends React.Component { const collapseBoolValue = this._chosenFacetsCollapse.get(facetHeader) this._chosenFacetsCollapse.set(facetHeader, !collapseBoolValue )})}> - + {this._chosenFacetsCollapse.get(facetHeader) ? : }
-- cgit v1.2.3-70-g09d2 From 9447ee01d501b3db69358b5b1526e640f2c54531 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Thu, 27 Jul 2023 13:02:19 -0400 Subject: filter - commented out slider and narrowing moving forward: need to change observable map to computed values --- src/client/documents/Documents.ts | 7 ++++++- src/client/views/FilterPanel.tsx | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5ef033e35..8cb4be72c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -96,7 +96,7 @@ class NumInfo extends FInfo { class StrInfo extends FInfo { fieldType? = 'string'; values?: string[] = []; - constructor(d: string, readOnly?: boolean, values?: string[]) { + constructor(d: string, readOnly?: boolean, values?: string[], filterable?:boolean) { super(d, readOnly); this.values = values; } @@ -138,9 +138,14 @@ class DateInfo extends FInfo { fieldType? = 'date'; values?: DateField[] = []; } +class ListInfo extends FInfo { + fieldType? = 'list'; + values?: List[] = []; +} type BOOLt = BoolInfo | boolean; type NUMt = NumInfo | number; type STRt = StrInfo | string; +type LISTt = ListInfo | List; type DOCt = DocInfo | Doc; type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; type PEVt = PEInfo | 'none' | 'all'; diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index b14e73208..ccd5253ff 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -117,6 +117,10 @@ export class FilterPanel extends React.Component { @observable _currentActiveFilters = new ObservableMap(); @observable _collapseReturnKeys = new Array(); + // @computed get _currentActiveFilters(){ + // return StrListCast(this.targetDoc.docFilters).map() + // } + // let returnKeys = []; @computed get activeFacets() { @@ -151,6 +155,40 @@ export class FilterPanel extends React.Component { if (facetHeader === 'text' || (facetValues.rtFields / allCollectionDocs.length > 0.1 && facetValues.strings.length > 20)) { this._chosenFacets.set(facetHeader, 'text'); } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) { + + // const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); + // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); + // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); + // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; + // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; + // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; + + // newFacet = Docs.Create.SliderDocument({ + // title: facetHeader, + // system: true, + // target: targetDoc, + // _fitWidth: true, + // _height: 40, + // _stayInCollection: true, + // treeViewExpandedView: 'layout', + // _treeViewOpen: true, + // _forceActive: true, + // _overflow: 'visible', + // }); + // const newFacetField = Doc.LayoutFieldKey(newFacet); + // const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); + // Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); + // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); + // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; + // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; + // Doc.GetProto(newFacet)[newFacetField + '-minThumb'] = extendedMinVal; + // Doc.GetProto(newFacet)[newFacetField + '-maxThumb'] = extendedMaxVal; + // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; + // newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: 'number' }); + // newFacet.data = ComputedField.MakeFunction(`readNumFacetData(self.target, self, "${FilterBox.targetDocChildKey}", "${facetHeader}")`); + + } else { this._chosenFacets.set(facetHeader, 'checkbox'); } -- cgit v1.2.3-70-g09d2 From caa2e09edb9c1db0af571013d006179bdc84f755 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 31 Jul 2023 12:58:06 -0400 Subject: All mapType stuff and constructor is done --- src/client/documents/Documents.ts | 3 ++ src/client/views/nodes/MapBox/MapBox.tsx | 70 +++++++++++++++++++++++-------- src/client/views/nodes/trails/PresBox.tsx | 9 +++- 3 files changed, 64 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5cced6d24..ca62028c1 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -221,6 +221,7 @@ export class DocumentOptions { openFactoryAsDelegate?: boolean; // lat?: NUMt = new NumInfo('latitude of a mapping view'); lng?: NUMt = new NumInfo('longitude of a mapping view'); + zoom?: NUMt = new NumInfo('zoom of a mapping view'); infoWindowOpen?: boolean; author?: string; _layout_fieldKey?: string; @@ -289,6 +290,8 @@ export class DocumentOptions { presPanY?: number; // panY saved as a view spec presLat?: NUMt = new NumInfo('latitude of a map'); // latitude of a map presLong?: NUMt = new NumInfo('longitude of map'); // longitude of map + presZoom?: NUMt = new NumInfo('zoom of map'); // zoom of map + presMapType?:string; presViewScale?: number; // viewScale saved as a view Spec presTransition?: number; //the time taken for the transition TO a document presDuration?: number; //the duration of the slide in presentation view diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 3c8e6203a..ad534f5f2 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -132,15 +132,21 @@ export class MapBox extends ViewBoxAnnotatableComponent ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude}), + this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}), (locationObject) => { - //TODO: SAVE ZOOM? VIEW STYLE? this._bingMap.current.setView({ + mapTypeId: locationObject.mapType, + zoom:locationObject.zoom, center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng), }); + // this._bingMap.current.setZoom(locationObject.zoom); + + }, {fireImmediately: true}); + + } componentWillUnmount(): void { @@ -678,7 +684,18 @@ export class MapBox extends ViewBoxAnnotatableComponent { this.dataDoc.latitude = this._bingMap.current.getCenter().latitude; this.dataDoc.longitude = this._bingMap.current.getCenter().longitude; + this.dataDoc.zoom = this._bingMap.current.getZoom(); + // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId(); }; + /* + * Updates maptype + */ + @action + updateMapType = () => { + this.dataDoc.mapType = this._bingMap.current.getMapTypeId(); + + }; + searched_pin: any; /* @@ -691,13 +708,18 @@ export class MapBox extends ViewBoxAnnotatableComponent { - this._bingMap.current.entities.clear(); - if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin); - // this.allMapPushpins.map(pin => this.addPushpin(pin)); - }; + // @action + // addAllPins = () => { + // this._bingMap.current.entities.clear(); + // if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin); + // // this.allMapPushpins.map(pin => this.addPushpin(pin)); + // }; /* * Returns doc w/ relevant info @@ -730,7 +752,9 @@ export class MapBox extends ViewBoxAnnotatableComponent{ - this._bingMap.current.entities.clear(); - this.allMapPushpins + // this._bingMap.current.entities.clear(); + // this.allMapPushpins this.allMapPushpins.map(pin => this.addPushpin(pin)); } @@ -798,7 +822,19 @@ export class MapBox extends ViewBoxAnnotatableComponent (this._bingMap = map.map); + /* + * Called when BingMap is first rendered + * Initializes starting values + */ + bingMapReady = (map: any) => { + this._bingMap = map.map; + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', this.updateLayout); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', this.updateMapType); + this.updateLayout(); + this.updateMapType(); + } + render() { const renderAnnotations = (docFilters?: () => string[]) => null; return ( diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index d1cfb86ae..0a907c958 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -464,7 +464,14 @@ export class PresBox extends ViewBoxBaseComponent() { } if (bestTarget.longitude !== activeItem.presLong) { Doc.SetInPlace(bestTarget, "longitude", NumCast(activeItem.presLong), true); - bestTarget.restoreTargetOn = true; + changed = true; + } + if (bestTarget.zoom !== activeItem.presZoom) { + Doc.SetInPlace(bestTarget, "zoom", NumCast(activeItem.presZoom), true); + changed = true; + } + if (bestTarget.mapType !== activeItem.presMapType) { + Doc.SetInPlace(bestTarget, "mapType", StrCast(activeItem.presMapType), true); changed = true; } } -- cgit v1.2.3-70-g09d2 From af5377f0e5bc69488c1620ae72b960f2a0f5d8f9 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Mon, 31 Jul 2023 12:58:32 -0400 Subject: filters - fixed bug on reload --- package-lock.json | 91 ++++++++++++++++++++++++++++++++++++++-- src/client/views/FilterPanel.tsx | 47 +++++++++++++-------- 2 files changed, 118 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 082531ea0..e08568816 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6902,12 +6902,97 @@ "strip-ansi": "^7.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "strip-ansi": { "version": "7.1.0", "bundled": true, "requires": { "ansi-regex": "^6.0.1" } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } } } }, @@ -8412,7 +8497,7 @@ } }, "string-width-cjs": { - "version": "npm:string-width@4.2.3", + "version": "npm:string-width-cjs@4.2.3", "bundled": true, "requires": { "emoji-regex": "^8.0.0", @@ -8435,7 +8520,7 @@ } }, "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", + "version": "npm:strip-ansi-cjs@6.0.1", "bundled": true, "requires": { "ansi-regex": "^5.0.1" @@ -8594,7 +8679,7 @@ } }, "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", + "version": "npm:wrap-ansi-cjs@7.0.0", "bundled": true, "requires": { "ansi-styles": "^4.0.0", diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index ccd5253ff..f85052ff2 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -89,6 +89,7 @@ export class FilterPanel extends React.Component { newarray = []; subDocs.forEach(t => { const facetVal = t[facetKey]; + // console.log("facetVal " + facetVal) if (facetVal instanceof RichTextField || typeof facetVal === 'string') rtFields++; facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field)); (facetVal === true || facetVal == false) && valueSet.add(Field.toString(!facetVal)); @@ -102,6 +103,7 @@ export class FilterPanel extends React.Component { } // } // }); + return { strings: Array.from(valueSet.keys()), rtFields }; } @@ -114,14 +116,20 @@ export class FilterPanel extends React.Component { @observable _chosenFacets = new ObservableMap(); @observable _chosenFacetsCollapse = new ObservableMap(); - @observable _currentActiveFilters = new ObservableMap(); @observable _collapseReturnKeys = new Array(); - // @computed get _currentActiveFilters(){ - // return StrListCast(this.targetDoc.docFilters).map() - // } - // let returnKeys = []; + // this computed function gets the active filters and maps them to their headers + + @computed get currentActiveFilterz(){ + const filters = new Map(); + //this.targetDoc.docFilters + StrListCast(this.targetDoc?._childFilters).map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] )) + console.log("what is wrong with filters " +filters ) + return filters + } + + // let returnKeys = []; @computed get activeFacets() { // console.log("chosen collpase " + this._chosenFacetsCollapse) @@ -202,11 +210,11 @@ export class FilterPanel extends React.Component { this._collapseReturnKeys.splice(0) for (var key of this.facetValues(facetHeader)){ - console.log("key : " + key ) - if (this._currentActiveFilters.get(key)){ + // console.log("key : " + key ) + if (this.currentActiveFilterz.get(key)){ + // console.log("WEREEE HERHEHHHHEHHHHEEE") this._collapseReturnKeys.push(key) }} - // return "hello" return (
@@ -273,12 +281,21 @@ export class FilterPanel extends React.Component {
{facetHeader.charAt(0).toUpperCase() + facetHeader.slice(1)} - {/*
*/}
{ + + for (const [key, value] of this.currentActiveFilterz) { + + console.log("NEW KEY " + key + "NEW VAL " + value); + + } + console.log("this is gather field values : " + this.gatherFieldValues(this.targetDocChildren, facetHeader)) + console.log("this is current facets: " + this.currentFacets) + + console.log("this is facet Header " + facetHeader + ". this is the get " + this.activeFacets.get(facetHeader)) const collapseBoolValue = this._chosenFacetsCollapse.get(facetHeader) this._chosenFacetsCollapse.set(facetHeader, !collapseBoolValue )})}> - + {this._chosenFacetsCollapse.get(facetHeader) ? : }
@@ -286,9 +303,8 @@ export class FilterPanel extends React.Component {
{ for (var key of this.facetValues(facetHeader)){ - if (this._currentActiveFilters.get(key)){ + if (this.currentActiveFilterz.get(key)){ Doc.setDocFilter(this.targetDoc, facetHeader, key, 'remove') - this._currentActiveFilters.delete(key) }} this.activeFacets.delete(facetHeader) @@ -332,6 +348,7 @@ export class FilterPanel extends React.Component { case 'checkbox': return this.facetValues(facetHeader).map(fval => { + const facetValue = fval; return (
@@ -345,10 +362,7 @@ export class FilterPanel extends React.Component { } type={type} onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} - onClick={undoable (e => - e.target.checked ? this._currentActiveFilters.set(fval, facetHeader) : this._currentActiveFilters.delete(fval) , 'set filter' - ) } - + /> {facetValue} @@ -360,4 +374,3 @@ export class FilterPanel extends React.Component { } -// NEED TO LEARN HOW TO RESET FILTERS WHEN WEBPAGE IS RELOADED \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 94f122332d15b5844a2c46fc78f9a3a3125e7b06 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 31 Jul 2023 14:02:02 -0400 Subject: Pushpin onclick centers on pushpin --- src/client/views/nodes/MapBox/MapBox.tsx | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index ad534f5f2..8e460c090 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -634,6 +634,14 @@ export class MapBox extends ViewBoxAnnotatableComponent { pin.infoWindowOpen = !pin.infoWindowOpen; + // TODO: + // if (sidebarannos is not open) open sidebarannos + + // pan to pushpin location + this.dataDoc.latitude = pin.lat; + this.dataDoc.longitude = pin.lng; + + // @action // onPointerDown = (e: React.PointerEvent) => { // if (e.button === 2 || e.ctrlKey) { @@ -685,6 +693,9 @@ export class MapBox extends ViewBoxAnnotatableComponent { - const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}); + const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), + // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'} + ); this._bingMap.current.entities.push(pushPin); @@ -806,9 +820,17 @@ export class MapBox extends ViewBoxAnnotatableComponent Date: Mon, 31 Jul 2023 15:41:53 -0400 Subject: filters - working on range --- src/client/views/FilterPanel.tsx | 16 ++++++++++------ src/fields/Doc.ts | 4 ++++ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index f85052ff2..d6638df46 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -164,12 +164,16 @@ export class FilterPanel extends React.Component { this._chosenFacets.set(facetHeader, 'text'); } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) { - // const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); - // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); - // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); - // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; - // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; - // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; + console.log("in this IF STATEMENE ") + + const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); + const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); + const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); + // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; + // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; + // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; + Doc.setDocRangeFilter(this.targetDoc, facetHeader) + // newFacet = Docs.Create.SliderDocument({ // title: facetHeader, diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 203f455db..578ee1caa 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1416,14 +1416,18 @@ export namespace Doc { } export function setDocRangeFilter(container: Opt, key: string, range?: number[]) { if (!container) return; + const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []); + console.log("hellllloooooooooooooooooooooooooo " + childFiltersByRanges.length) for (let i = 0; i < childFiltersByRanges.length; i += 3) { + console.log("inside if statement") if (childFiltersByRanges[i] === key) { childFiltersByRanges.splice(i, 3); break; } } if (range !== undefined) { + console.log("in doc.ts in set range filter") childFiltersByRanges.push(key); childFiltersByRanges.push(range[0].toString()); childFiltersByRanges.push(range[1].toString()); -- cgit v1.2.3-70-g09d2 From fde0f815b3459d39b642d66c1c4466d9a9504979 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Tue, 1 Aug 2023 12:53:35 -0400 Subject: filters - bobs comments for future changes --- src/client/views/FilterPanel.tsx | 115 +++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index d6638df46..ce6e2b1f3 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -65,13 +65,35 @@ export class FilterPanel extends React.Component { return [...noviceFields, ...sortedKeys]; } + @computed get rangeFilters() { + return StrListCast(this.targetDoc?._childFiltersByRanges).filter((filter, i) => !( i % 3) ) + } + /** - * The current attributes selected to filter based on + * activeFilters( ) -- all filters that currently have a filter set on them in this document (ranges, and others) + * ["#tags::bob::check", "tags::joe::check", "width", "height"] */ @computed get activeFilters() { - return StrListCast(this.targetDoc?._childFilters); + return StrListCast(this.targetDoc?._childFilters).concat(this.rangeFilters); } + // + // activeFacetHeaders() - just the facet names, not the rest of the filter + // + // this wants to return all the filter facets that have an existing filter set on them in order to show them in the rendered panel + // this set may overlap the selectedFilters + // if the components reloads, these will still exist and be shown + + // ["#tags", "width", "height"] + // + @computed get currentActiveFilterz(){ // this.existingFilters + const filters = new Map(); + //this.targetDoc.docFilters + this.activeFilters.map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] )) + console.log("what is wrong with filters " +filters ) + return filters + } + /** * @returns a string array of the current attributes */ @@ -114,36 +136,55 @@ export class FilterPanel extends React.Component { Doc.setDocRangeFilter(this.targetDoc, filterName, undefined); }; - @observable _chosenFacets = new ObservableMap(); + // @observable _chosenFacets = new ObservableMap(); @observable _chosenFacetsCollapse = new ObservableMap(); @observable _collapseReturnKeys = new Array(); // this computed function gets the active filters and maps them to their headers - @computed get currentActiveFilterz(){ - const filters = new Map(); - //this.targetDoc.docFilters - StrListCast(this.targetDoc?._childFilters).map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] )) - console.log("what is wrong with filters " +filters ) - return filters - } - - // let returnKeys = []; + // + // activeRenderedFacetInfos() + // returns renderInfo for all user selected filters and for all existing filters set on the document + // Map("tags" => {"checkbox"}, + // "width" => {"rangs", domain:[1978,1992]}) + // + @computed get activeFacets() { // selectedFacetRenderInfo => []:{facetName, renderInfo}[] renderInfo: { renderType: text|range|... , minVal?: , maxVal?; } + + // return new Set( + // Array.from(new Set(Array.from(this.selectedFacetHeaders).concat(this.existingFacetHeaders))) + // .map(facetHeader => { --> getting exisitng filters and new filters that havent been selected but need to remove duplicates + // most of what facetClick did before ... + // minVal, maxvval... + // if (...) + // return {key: facet, renderType: "text"} + // else if (... numbers) return {key:facet, renderrType: range, extendedMinVal, maxval + // return + // return .. + // })) - @computed get activeFacets() { // console.log("chosen collpase " + this._chosenFacetsCollapse) - const facets = new Map(this._chosenFacets); - StrListCast(this.targetDoc?._childFilters).map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')); - setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')))); + const facets = new Map(); + this.activeFilters.map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')); + // setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')))); return facets; } + @observable selectedFacetHeaders = new Set(); + /** - * Responds to clicking the check box in the flyout menu + * user clicks on a filter facet because they want to see it. + * this adds this chosen filter to a set of user selected filters called: selectedFilters + * if this component reloads, then these filters will go away since they haven't been written to any Doc anywhere + * + * // this._selectedFacets.add(facetHeader); .. add to Set() not array */ @action - facetClick = (facetHeader: string) => { + facetClick = (facetHeader: string) => { // just when someone chooses a facet + + // return; + + if (!this.targetDoc) return; const allCollectionDocs = this.targetDocChildren; const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader); @@ -161,18 +202,20 @@ export class FilterPanel extends React.Component { } }); if (facetHeader === 'text' || (facetValues.rtFields / allCollectionDocs.length > 0.1 && facetValues.strings.length > 20)) { - this._chosenFacets.set(facetHeader, 'text'); + // this._chosenFacets.set(facetHeader, 'text'); } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) { console.log("in this IF STATEMENE ") - const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); + const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); - // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; + // this.targetDoc["year-min"] = 1978 + // // facetHeader == year + // this.targetDoc[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; - Doc.setDocRangeFilter(this.targetDoc, facetHeader) + Doc.setDocRangeFilter(this.targetDoc, facetHeader, [extendedMinVal, extendedMaxVal] ) // newFacet = Docs.Create.SliderDocument({ @@ -202,7 +245,7 @@ export class FilterPanel extends React.Component { } else { - this._chosenFacets.set(facetHeader, 'checkbox'); + // this._chosenFacets.set(facetHeader, 'checkbox'); } this._chosenFacetsCollapse.set(facetHeader, false) }; @@ -279,7 +322,7 @@ export class FilterPanel extends React.Component {
- {Array.from(this.activeFacets.keys()).map(facetHeader => ( + {Array.from(this.activeFacets.keys()).map(facetHeader => ( // iterato over activeFacetRenderInfos ==> renderInfo which you can renderInfo.facetHeader
@@ -312,7 +355,7 @@ export class FilterPanel extends React.Component { }} this.activeFacets.delete(facetHeader) - this._chosenFacets.delete(facetHeader) + // this._chosenFacets.delete(facetHeader) this._chosenFacetsCollapse.delete(facetHeader) })} > @@ -326,7 +369,7 @@ export class FilterPanel extends React.Component { //
{this.sortingCurrentFacetValues(facetHeader)}
&& this._collapseReturnKeys.splice(0) this.sortingCurrentFacetValues(facetHeader) // && this._collapseReturnKeys.splice(0) - : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } + : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } // pass renderInfo from iterator {/* */}
))} @@ -336,8 +379,9 @@ export class FilterPanel extends React.Component { } private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string): React.ReactNode { - switch (type) { - case 'text': + // displayFacetValueFilterUIs(renderIinfo) + switch (type /* renderInfo.type */ ) { + case 'text': // if (this.chosenFacets.get(facetHeader) === 'text') return ( { {facetValue}
); - }); + }) + // case 'range' + // return domain is number[] for min and max + // onChange = { ... Doc.setDocRangeFilter(this.targetDoc, facetHeader, [extendedMinVal, extendedMaxVal] ) } + // + // OR + + // return
+ // // domain is number[] for min and max + // + // Date: Tue, 1 Aug 2023 13:40:37 -0400 Subject: Pin onclick remove fully integrate, working but not tested a lot --- src/client/views/nodes/MapBox/MapBox.tsx | 67 +++++++++++++++++++------ src/client/views/nodes/MapBox/MapPushpinBox.tsx | 2 +- 2 files changed, 52 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 8e460c090..02fa31d9e 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -140,11 +140,36 @@ export class MapBox extends ViewBoxAnnotatableComponent ({pinList:this.dataDoc[this.annotationKey]}), + // (pinsObject) => { + // this._bingMap.current.entities.clear(); + // pinsObject.pinList.map((pushpin: Doc) => ( + // new ScriptField(undefined)} + // onKey={undefined} + // onDoubleClick={undefined} + // onBrowseClick={undefined} + // docFilters={returnEmptyDoclist} + // docRangeFilters={returnEmptyDoclist} + // searchFilterDocs={returnEmptyDoclist} + // isDocumentActive={returnFalse} + // isContentActive={returnFalse} + // addDocTab={returnFalse} + // ScreenToLocalTransform={()=>new Transform(0,0,0)} + // fitContentsToBox={undefined} + // focus={returnOne} + // />)); + + // }, {fireImmediately: false}); } @@ -615,15 +640,16 @@ export class MapBox extends ViewBoxAnnotatableComponent { // Stores the pushpin as a MapMarkerDocument - const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {}); + const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {},'pushpinIDamongus'+this.incrementer); this.addDocument(mapMarker, this.annotationKey); + this.incrementer++; // mapMarker.infoWindowOpen = true; }; @@ -631,15 +657,20 @@ export class MapBox extends ViewBoxAnnotatableComponent { - pin.infoWindowOpen = !pin.infoWindowOpen; - + @action + pushpinClicked = (pinDoc:Doc,pin:any) => { // TODO: // if (sidebarannos is not open) open sidebarannos + // creates button onclick removes the doc from annotations // pan to pushpin location - this.dataDoc.latitude = pin.lat; - this.dataDoc.longitude = pin.lng; + this.dataDoc.latitude = pinDoc.lat; + this.dataDoc.longitude = pinDoc.lng; + // this.dataDoc[this.annotationKey].pop(pin); + this.removePushpin(pinDoc,pin); + + + // @action @@ -792,19 +823,22 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(pin)); + this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin,pushPin)); } /* * Input: pin doc - * Adds MicrosoftMaps Pushpin to the map (render) + * Removes MicrosoftMaps Pushpin to the map (render) */ @action - removePushpin = (pin:any)=>{ - // this._bingMap.current.entities.clear(); + removePushpin = (pinDoc:Doc,pin:any)=>{ // this.allMapPushpins - this.allMapPushpins.map(pin => this.addPushpin(pin)); + // this.allMapPushpins.map(pin => this.addPushpin(pin)); + // this._bingMap.current.entities.clear(); + this._bingMap.current.entities.remove(pin); + this.removeDocument(pinDoc, this.annotationKey); + + // this.dataDoc[this.annotationKey] } /** @@ -889,6 +923,7 @@ export class MapBox extends ViewBoxAnnotatableComponent
+ {this.allMapPushpins .map(pushpin => ( () { this.mapBoxView.addPushpin(this.rootDoc); } componentWillUnmount() { - this.mapBoxView.removePushpin(this.rootDoc); + // this.mapBoxView.removePushpin(this.rootDoc); } -- cgit v1.2.3-70-g09d2 From e1968a5f809629305f4358fe3942ad38c15e0ff2 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 1 Aug 2023 14:04:52 -0400 Subject: omg im so tired but yay remove pin works --- src/client/views/nodes/MapBox/MapBox.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 02fa31d9e..61296c6c8 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -826,6 +826,10 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(pin,pushPin)); } + + @observable + pinIsSelected_TEMPORARY:boolean=false; // toggles if remove pin button appears + /* * Input: pin doc * Removes MicrosoftMaps Pushpin to the map (render) @@ -858,6 +862,7 @@ export class MapBox extends ViewBoxAnnotatableComponent {this.placePinOn ? : } - + {/* {this.pinIsSelected_TEMPORARY? : null} */}
-- cgit v1.2.3-70-g09d2 From d9f9eb5a89b14a3f9f6847ecd405c2142058b0d5 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Tue, 1 Aug 2023 16:04:46 -0400 Subject: filter - made bobs comments with basic slider implementation --- src/client/views/FilterPanel.tsx | 349 ++++++++++++++++++++++++++------------- src/fields/Doc.ts | 4 +- 2 files changed, 233 insertions(+), 120 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index ce6e2b1f3..b7718c6a3 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -13,6 +13,8 @@ import { SearchBox } from './search/SearchBox'; import { undoable } from '../util/UndoManager'; import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai'; import { CiCircleRemove } from 'react-icons/ci'; +import { Slider, Rail, Handles, Tracks, Ticks } from 'react-compound-slider'; +import { TooltipRail, Handle, Tick, Track } from './nodes/SliderBox-components'; //slight bug when you don't click on background canvas before creating filter and the you click on the canvas @@ -77,6 +79,14 @@ export class FilterPanel extends React.Component { return StrListCast(this.targetDoc?._childFilters).concat(this.rangeFilters); } + @computed get mapActiveFiltersToFacets() { + const filters = new Map(); + //this.targetDoc.docFilters + this.activeFilters.map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] )) + console.log("what is wrong with filters " +filters ) + return filters + } + // // activeFacetHeaders() - just the facet names, not the rest of the filter // @@ -86,20 +96,31 @@ export class FilterPanel extends React.Component { // ["#tags", "width", "height"] // - @computed get currentActiveFilterz(){ // this.existingFilters - const filters = new Map(); - //this.targetDoc.docFilters - this.activeFilters.map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] )) - console.log("what is wrong with filters " +filters ) - return filters + + @computed get activeFacetHeaders() { + const activeHeaders = new Array() + + this.activeFilters.map(filter => activeHeaders.push(filter.split(Doc.FilterSep)[0]) ) + + + return activeHeaders; } + + + // @computed get currentActiveFilterz(){ // this.existingFilters + // const filters = new Map(); + // //this.targetDoc.docFilters + // this.activeFilters.map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] )) + // console.log("what is wrong with filters " +filters ) + // return filters + // } /** * @returns a string array of the current attributes */ - @computed get currentFacets() { - return this.activeFilters.map(filter => filter.split(Doc.FilterSep)[0]); - } + // @computed get currentFacets() { + // return this.activeFilters.map(filter => filter.split(Doc.FilterSep)[0]); + // } gatherFieldValues(childDocs: Doc[], facetKey: string) { const valueSet = new Set(); @@ -149,28 +170,68 @@ export class FilterPanel extends React.Component { // Map("tags" => {"checkbox"}, // "width" => {"rangs", domain:[1978,1992]}) // - @computed get activeFacets() { // selectedFacetRenderInfo => []:{facetName, renderInfo}[] renderInfo: { renderType: text|range|... , minVal?: , maxVal?; } - - // return new Set( - // Array.from(new Set(Array.from(this.selectedFacetHeaders).concat(this.existingFacetHeaders))) - // .map(facetHeader => { --> getting exisitng filters and new filters that havent been selected but need to remove duplicates - // most of what facetClick did before ... - // minVal, maxvval... - // if (...) - // return {key: facet, renderType: "text"} - // else if (... numbers) return {key:facet, renderrType: range, extendedMinVal, maxval - // return - // return .. - // })) - - // console.log("chosen collpase " + this._chosenFacetsCollapse) - const facets = new Map(); - this.activeFilters.map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')); - // setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')))); - return facets; + + @computed get activeRenderedFacetInfos(){ + + return new Set( + Array.from(new Set(Array.from(this._selectedFacetHeaders).concat(this.activeFacetHeaders))).map( + facetHeader => { + + const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader); + + let nonNumbers = 0; + let minVal = Number.MAX_VALUE, + maxVal = -Number.MAX_VALUE; + facetValues.strings.map(val => { + const num = val ? Number(val) : Number.NaN; + if (Number.isNaN(num)) { + val && nonNumbers++; + } else { + minVal = Math.min(num, minVal); + maxVal = Math.max(num, maxVal); + } + }); + + + if (facetHeader === 'text' ){ + return {facetHeader: facetHeader, renderType: 'text'} + + } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1){ + + const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); + const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); + const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter + return {facetHeader: facetHeader, renderType: 'range', domain: [extendedMinVal, extendedMaxVal], range: ranged ? ranged: [extendedMinVal, extendedMaxVal]} + + } else{ + return {facetHeader: facetHeader, renderType: 'checkbox'} + } + } + )) } - @observable selectedFacetHeaders = new Set(); + // @computed get activeFacets() { // selectedFacetRenderInfo => []:{facetName, renderInfo}[] renderInfo: { renderType: text|range|... , minVal?: , maxVal?; } + + // // return new Set( + // // Array.from(new Set(Array.from(this.selectedFacetHeaders).concat(this.existingFacetHeaders))) + // // .map(facetHeader => { --> getting exisitng filters and new filters that havent been selected but need to remove duplicates + // // most of what facetClick did before ... + // // minVal, maxvval... + // // if (...) + // // return {key: facet, renderType: "text"} + // // else if (... numbers) return {key:facet, renderrType: range, extendedMinVal, maxval + // // return + // // return .. + // // })) + + // // console.log("chosen collpase " + this._chosenFacetsCollapse) + // const facets = new Map(); + // this.activeFilters.map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')); + // // setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')))); + // return facets; + // } + + @observable _selectedFacetHeaders = new Set(); /** * user clicks on a filter facet because they want to see it. @@ -182,72 +243,74 @@ export class FilterPanel extends React.Component { @action facetClick = (facetHeader: string) => { // just when someone chooses a facet - // return; + this._selectedFacetHeaders.add(facetHeader); + return - if (!this.targetDoc) return; - const allCollectionDocs = this.targetDocChildren; - const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader); - let nonNumbers = 0; - let minVal = Number.MAX_VALUE, - maxVal = -Number.MAX_VALUE; - facetValues.strings.map(val => { - const num = val ? Number(val) : Number.NaN; - if (Number.isNaN(num)) { - val && nonNumbers++; - } else { - minVal = Math.min(num, minVal); - maxVal = Math.max(num, maxVal); - } - }); - if (facetHeader === 'text' || (facetValues.rtFields / allCollectionDocs.length > 0.1 && facetValues.strings.length > 20)) { - // this._chosenFacets.set(facetHeader, 'text'); - } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) { - - console.log("in this IF STATEMENE ") - - const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter - const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); - const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); - // this.targetDoc["year-min"] = 1978 - // // facetHeader == year - // this.targetDoc[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; - // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; - // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; - Doc.setDocRangeFilter(this.targetDoc, facetHeader, [extendedMinVal, extendedMaxVal] ) - - - // newFacet = Docs.Create.SliderDocument({ - // title: facetHeader, - // system: true, - // target: targetDoc, - // _fitWidth: true, - // _height: 40, - // _stayInCollection: true, - // treeViewExpandedView: 'layout', - // _treeViewOpen: true, - // _forceActive: true, - // _overflow: 'visible', - // }); - // const newFacetField = Doc.LayoutFieldKey(newFacet); - // const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); - // Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox - // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); - // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); - // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; - // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; - // Doc.GetProto(newFacet)[newFacetField + '-minThumb'] = extendedMinVal; - // Doc.GetProto(newFacet)[newFacetField + '-maxThumb'] = extendedMaxVal; - // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; - // newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: 'number' }); - // newFacet.data = ComputedField.MakeFunction(`readNumFacetData(self.target, self, "${FilterBox.targetDocChildKey}", "${facetHeader}")`); + // if (!this.targetDoc) return; + // const allCollectionDocs = this.targetDocChildren; + // const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader); + + // let nonNumbers = 0; + // let minVal = Number.MAX_VALUE, + // maxVal = -Number.MAX_VALUE; + // facetValues.strings.map(val => { + // const num = val ? Number(val) : Number.NaN; + // if (Number.isNaN(num)) { + // val && nonNumbers++; + // } else { + // minVal = Math.min(num, minVal); + // maxVal = Math.max(num, maxVal); + // } + // }); + // if (facetHeader === 'text' || (facetValues.rtFields / allCollectionDocs.length > 0.1 && facetValues.strings.length > 20)) { + // // this._chosenFacets.set(facetHeader, 'text'); + // } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) { + + // console.log("in this IF STATEMENE ") + + // const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter + // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); + // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); + // // this.targetDoc["year-min"] = 1978 + // // // facetHeader == year + // // this.targetDoc[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; + // // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; + // // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; + // Doc.setDocRangeFilter(this.targetDoc, facetHeader, [extendedMinVal, extendedMaxVal] ) + + + // // newFacet = Docs.Create.SliderDocument({ + // // title: facetHeader, + // // system: true, + // // target: targetDoc, + // // _fitWidth: true, + // // _height: 40, + // // _stayInCollection: true, + // // treeViewExpandedView: 'layout', + // // _treeViewOpen: true, + // // _forceActive: true, + // // _overflow: 'visible', + // // }); + // // const newFacetField = Doc.LayoutFieldKey(newFacet); + // // const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); + // // Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + // // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); + // // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); + // // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; + // // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; + // // Doc.GetProto(newFacet)[newFacetField + '-minThumb'] = extendedMinVal; + // // Doc.GetProto(newFacet)[newFacetField + '-maxThumb'] = extendedMaxVal; + // // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; + // // newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: 'number' }); + // // newFacet.data = ComputedField.MakeFunction(`readNumFacetData(self.target, self, "${FilterBox.targetDocChildKey}", "${facetHeader}")`); - } else { - // this._chosenFacets.set(facetHeader, 'checkbox'); - } - this._chosenFacetsCollapse.set(facetHeader, false) + // } else { + // // this._chosenFacets.set(facetHeader, 'checkbox'); + // } + // this._chosenFacetsCollapse.set(facetHeader, false) }; @@ -256,12 +319,13 @@ export class FilterPanel extends React.Component { this._collapseReturnKeys.splice(0) - for (var key of this.facetValues(facetHeader)){ + for (var key of this.facetValues(facetHeader)){ // all filters currently set // console.log("key : " + key ) - if (this.currentActiveFilterz.get(key)){ + if (this.mapActiveFiltersToFacets.get(key)){ // work with the current filter selected // console.log("WEREEE HERHEHHHHEHHHHEEE") this._collapseReturnKeys.push(key) - }} + } + } return (
@@ -301,7 +365,8 @@ export class FilterPanel extends React.Component { render() { - const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); + // const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); + const options = this._allFacets.filter(facet => this.activeFacetHeaders.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); return (
@@ -322,41 +387,42 @@ export class FilterPanel extends React.Component {
- {Array.from(this.activeFacets.keys()).map(facetHeader => ( // iterato over activeFacetRenderInfos ==> renderInfo which you can renderInfo.facetHeader + {Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => ( // iterato over activeFacetRenderInfos ==> renderInfo which you can renderInfo.facetHeader
- {facetHeader.charAt(0).toUpperCase() + facetHeader.slice(1)} + {renderInfo.facetHeader.charAt(0).toUpperCase() + renderInfo.facetHeader.slice(1)}
{ - for (const [key, value] of this.currentActiveFilterz) { - - console.log("NEW KEY " + key + "NEW VAL " + value); - - } - console.log("this is gather field values : " + this.gatherFieldValues(this.targetDocChildren, facetHeader)) - console.log("this is current facets: " + this.currentFacets) - - console.log("this is facet Header " + facetHeader + ". this is the get " + this.activeFacets.get(facetHeader)) - const collapseBoolValue = this._chosenFacetsCollapse.get(facetHeader) - this._chosenFacetsCollapse.set(facetHeader, !collapseBoolValue )})}> + + const collapseBoolValue = this._chosenFacetsCollapse.get(renderInfo.facetHeader) + this._chosenFacetsCollapse.set(renderInfo.facetHeader, !collapseBoolValue )})}> - {this._chosenFacetsCollapse.get(facetHeader) ? : } + {this._chosenFacetsCollapse.get(renderInfo.facetHeader) ? : }
{ - for (var key of this.facetValues(facetHeader)){ - if (this.currentActiveFilterz.get(key)){ - Doc.setDocFilter(this.targetDoc, facetHeader, key, 'remove') + for (var key of this.facetValues(renderInfo.facetHeader)){ + if (this.mapActiveFiltersToFacets.get(key)){ + Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, key, 'remove') }} - this.activeFacets.delete(facetHeader) + if (renderInfo.facetHeader) + + this._selectedFacetHeaders.delete(renderInfo.facetHeader) + + // this.activeFacets.delete(renderInfo.facetHeader) // this._chosenFacets.delete(facetHeader) - this._chosenFacetsCollapse.delete(facetHeader) + this._chosenFacetsCollapse.delete(renderInfo.facetHeader) + + console.log("this is activeFilters " + this.activeFilters) + console.log("this is activeFacetHeaders " + this.activeFacetHeaders) + console.log("thsi is activeRenderedFacetInfos " + this.activeRenderedFacetInfos) + console.log("thsi is selected facet Headers " + this._selectedFacetHeaders ) })} > @@ -365,11 +431,11 @@ export class FilterPanel extends React.Component {
- { this._chosenFacetsCollapse.get(facetHeader) ? + { this._chosenFacetsCollapse.get(renderInfo.facetHeader) ? //
{this.sortingCurrentFacetValues(facetHeader)}
&& this._collapseReturnKeys.splice(0) - this.sortingCurrentFacetValues(facetHeader) + this.sortingCurrentFacetValues(renderInfo.facetHeader) // && this._collapseReturnKeys.splice(0) - : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) } // pass renderInfo from iterator + : this.displayFacetValueFilterUIs(renderInfo.renderType, renderInfo.facetHeader, renderInfo.domain, renderInfo.range ) } {/* */}
))} @@ -378,7 +444,7 @@ export class FilterPanel extends React.Component { ); } - private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string): React.ReactNode { + private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string, renderInfoDomain?: number[] | undefined, renderInfoRange?: number[] ): React.ReactNode { // displayFacetValueFilterUIs(renderIinfo) switch (type /* renderInfo.type */ ) { case 'text': // if (this.chosenFacets.get(facetHeader) === 'text') @@ -417,6 +483,55 @@ export class FilterPanel extends React.Component {
); }) + + case 'range': + console.log("in range") + + const domain = renderInfoDomain; + + if (domain){ + return( + Doc.setDocRangeFilter(this.targetDoc, facetHeader, values)} values={renderInfoRange!} > + {railProps => } + + {({ handles, activeHandleID, getHandleProps }) => ( +
+ {handles.map((handle, i) => { + // const value = i === 0 ? defaultValues[0] : defaultValues[1]; + return ( +
+ +
+ ); + })} +
+ )} +
+ + {({ tracks, getTrackProps }) => ( +
+ {tracks.map(({ id, source, target }) => ( + + ))} +
+ )} +
+ + {({ ticks }) => ( +
+ {ticks.map(tick => ( + val.toString()} /> + ))} +
+ )} +
+
+ ) + } + + + // case 'range' // return domain is number[] for min and max diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 578ee1caa..d07028ec2 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1414,11 +1414,10 @@ export namespace Doc { prevLayout === 'icon' && (doc.deiconifyLayout = undefined); doc.layout_fieldKey = deiconify || 'layout'; } - export function setDocRangeFilter(container: Opt, key: string, range?: number[]) { + export function setDocRangeFilter(container: Opt, key: string, range?: readonly number[]) { if (!container) return; const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []); - console.log("hellllloooooooooooooooooooooooooo " + childFiltersByRanges.length) for (let i = 0; i < childFiltersByRanges.length; i += 3) { console.log("inside if statement") if (childFiltersByRanges[i] === key) { @@ -1441,7 +1440,6 @@ export namespace Doc { // 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) { - console.log("in setDocFilter: key "+ key + "value " + value) if (!container) return; const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters'; const childFilters = StrListCast(container[filterField]); -- cgit v1.2.3-70-g09d2 From 8d85780411df77db6150b88f0d8272f495c7fdb1 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Wed, 2 Aug 2023 12:58:34 -0400 Subject: filter - slider works (w/o UI and collapse) --- src/client/views/FilterPanel.scss | 22 ++++ src/client/views/FilterPanel.tsx | 228 ++++++++++---------------------------- src/fields/Doc.ts | 19 +++- 3 files changed, 99 insertions(+), 170 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss index 78e7904b8..b18b01325 100644 --- a/src/client/views/FilterPanel.scss +++ b/src/client/views/FilterPanel.scss @@ -231,6 +231,28 @@ } +// .sliderBox-outerDiv { +// width: 30%;// width: calc(100% - 14px); // 14px accounts for handles that are at the max value of the slider that would extend outside the box +// height: 40; // height: 100%; +// border-radius: inherit; +// display: flex; +// flex-direction: column; +// position: relative; +// // background-color: yellow; +// .slider-tracks { +// top: 7px; +// position: relative; +// } +// .slider-ticks { +// position: relative; +// } +// .slider-handles { +// top: 7px; +// position: relative; +// } +// } + + diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index b7718c6a3..54f5122b4 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -83,7 +83,6 @@ export class FilterPanel extends React.Component { const filters = new Map(); //this.targetDoc.docFilters this.activeFilters.map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] )) - console.log("what is wrong with filters " +filters ) return filters } @@ -99,22 +98,11 @@ export class FilterPanel extends React.Component { @computed get activeFacetHeaders() { const activeHeaders = new Array() - this.activeFilters.map(filter => activeHeaders.push(filter.split(Doc.FilterSep)[0]) ) return activeHeaders; } - - - // @computed get currentActiveFilterz(){ // this.existingFilters - // const filters = new Map(); - // //this.targetDoc.docFilters - // this.activeFilters.map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] )) - // console.log("what is wrong with filters " +filters ) - // return filters - // } - /** * @returns a string array of the current attributes */ @@ -210,27 +198,6 @@ export class FilterPanel extends React.Component { )) } - // @computed get activeFacets() { // selectedFacetRenderInfo => []:{facetName, renderInfo}[] renderInfo: { renderType: text|range|... , minVal?: , maxVal?; } - - // // return new Set( - // // Array.from(new Set(Array.from(this.selectedFacetHeaders).concat(this.existingFacetHeaders))) - // // .map(facetHeader => { --> getting exisitng filters and new filters that havent been selected but need to remove duplicates - // // most of what facetClick did before ... - // // minVal, maxvval... - // // if (...) - // // return {key: facet, renderType: "text"} - // // else if (... numbers) return {key:facet, renderrType: range, extendedMinVal, maxval - // // return - // // return .. - // // })) - - // // console.log("chosen collpase " + this._chosenFacetsCollapse) - // const facets = new Map(); - // this.activeFilters.map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')); - // // setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox')))); - // return facets; - // } - @observable _selectedFacetHeaders = new Set(); /** @@ -240,105 +207,40 @@ export class FilterPanel extends React.Component { * * // this._selectedFacets.add(facetHeader); .. add to Set() not array */ + @action facetClick = (facetHeader: string) => { // just when someone chooses a facet this._selectedFacetHeaders.add(facetHeader); return - - - // if (!this.targetDoc) return; - // const allCollectionDocs = this.targetDocChildren; - // const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader); - - // let nonNumbers = 0; - // let minVal = Number.MAX_VALUE, - // maxVal = -Number.MAX_VALUE; - // facetValues.strings.map(val => { - // const num = val ? Number(val) : Number.NaN; - // if (Number.isNaN(num)) { - // val && nonNumbers++; - // } else { - // minVal = Math.min(num, minVal); - // maxVal = Math.max(num, maxVal); - // } - // }); - // if (facetHeader === 'text' || (facetValues.rtFields / allCollectionDocs.length > 0.1 && facetValues.strings.length > 20)) { - // // this._chosenFacets.set(facetHeader, 'text'); - // } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) { - - // console.log("in this IF STATEMENE ") - - // const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter - // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); - // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); - // // this.targetDoc["year-min"] = 1978 - // // // facetHeader == year - // // this.targetDoc[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; - // // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; - // // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; - // Doc.setDocRangeFilter(this.targetDoc, facetHeader, [extendedMinVal, extendedMaxVal] ) - - - // // newFacet = Docs.Create.SliderDocument({ - // // title: facetHeader, - // // system: true, - // // target: targetDoc, - // // _fitWidth: true, - // // _height: 40, - // // _stayInCollection: true, - // // treeViewExpandedView: 'layout', - // // _treeViewOpen: true, - // // _forceActive: true, - // // _overflow: 'visible', - // // }); - // // const newFacetField = Doc.LayoutFieldKey(newFacet); - // // const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); - // // Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox - // // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1)); - // // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05))); - // // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0]; - // // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1]; - // // Doc.GetProto(newFacet)[newFacetField + '-minThumb'] = extendedMinVal; - // // Doc.GetProto(newFacet)[newFacetField + '-maxThumb'] = extendedMaxVal; - // // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`; - // // newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: 'number' }); - // // newFacet.data = ComputedField.MakeFunction(`readNumFacetData(self.target, self, "${FilterBox.targetDocChildKey}", "${facetHeader}")`); - - - // } else { - // // this._chosenFacets.set(facetHeader, 'checkbox'); - // } - // this._chosenFacetsCollapse.set(facetHeader, false) }; @action sortingCurrentFacetValues = (facetHeader:string) => { - + console.log("in function to begin with") this._collapseReturnKeys.splice(0) - for (var key of this.facetValues(facetHeader)){ // all filters currently set - // console.log("key : " + key ) - if (this.mapActiveFiltersToFacets.get(key)){ // work with the current filter selected - // console.log("WEREEE HERHEHHHHEHHHHEEE") - this._collapseReturnKeys.push(key) - } - } + console.log("this si sfacetValies " + this.facetValues(facetHeader)) - return (
- - {this._collapseReturnKeys.join(', ') } - {/* .toString()} */} -
) - + //if range then display range values + + // if(Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => (renderInfo.renderType === "range" && renderInfo.facetHeader === facetHeader ))){ + // console.log("JOE MAMA") + // } + for (var key of this.facetValues(facetHeader)){ + if (this.mapActiveFiltersToFacets.get(key)){ + this._collapseReturnKeys.push(key) + }} + return ( +
+ {this._collapseReturnKeys.join(', ') } +
) } - - facetValues = (facetHeader: string) => { const allCollectionDocs = new Set(); SearchBox.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc)); @@ -365,7 +267,6 @@ export class FilterPanel extends React.Component { render() { - // const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); const options = this._allFacets.filter(facet => this.activeFacetHeaders.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); return ( @@ -410,19 +311,20 @@ export class FilterPanel extends React.Component { if (this.mapActiveFiltersToFacets.get(key)){ Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, key, 'remove') }} - - if (renderInfo.facetHeader) - this._selectedFacetHeaders.delete(renderInfo.facetHeader) - - // this.activeFacets.delete(renderInfo.facetHeader) - // this._chosenFacets.delete(facetHeader) this._chosenFacetsCollapse.delete(renderInfo.facetHeader) + Doc.setDocRangeFilter(this.targetDoc, renderInfo.facetHeader, renderInfo.domain, 'remove') + + console.log("this is activeFilters " + this.activeFilters) console.log("this is activeFacetHeaders " + this.activeFacetHeaders) console.log("thsi is activeRenderedFacetInfos " + this.activeRenderedFacetInfos) console.log("thsi is selected facet Headers " + this._selectedFacetHeaders ) + console.log("THIS IS THE ONE ADDED "+ this.targetDoc?._childFiltersByRanges) + + + })} > @@ -432,9 +334,7 @@ export class FilterPanel extends React.Component {
{ this._chosenFacetsCollapse.get(renderInfo.facetHeader) ? - //
{this.sortingCurrentFacetValues(facetHeader)}
&& this._collapseReturnKeys.splice(0) this.sortingCurrentFacetValues(renderInfo.facetHeader) - // && this._collapseReturnKeys.splice(0) : this.displayFacetValueFilterUIs(renderInfo.renderType, renderInfo.facetHeader, renderInfo.domain, renderInfo.range ) } {/* */}
@@ -445,7 +345,6 @@ export class FilterPanel extends React.Component { } private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string, renderInfoDomain?: number[] | undefined, renderInfoRange?: number[] ): React.ReactNode { - // displayFacetValueFilterUIs(renderIinfo) switch (type /* renderInfo.type */ ) { case 'text': // if (this.chosenFacets.get(facetHeader) === 'text') return ( @@ -460,9 +359,7 @@ export class FilterPanel extends React.Component { /> ); case 'checkbox': - return this.facetValues(facetHeader).map(fval => { - const facetValue = fval; return (
@@ -472,12 +369,9 @@ export class FilterPanel extends React.Component { StrListCast(this.targetDoc._childFilters) .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue) ?.split(Doc.FilterSep)[2] === 'check' - } type={type} onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} - - /> {facetValue}
@@ -485,48 +379,48 @@ export class FilterPanel extends React.Component { }) case 'range': - console.log("in range") - const domain = renderInfoDomain; - if (domain){ return( - Doc.setDocRangeFilter(this.targetDoc, facetHeader, values)} values={renderInfoRange!} > - {railProps => } - - {({ handles, activeHandleID, getHandleProps }) => ( -
- {handles.map((handle, i) => { - // const value = i === 0 ? defaultValues[0] : defaultValues[1]; - return ( -
- +
+ Doc.setDocRangeFilter(this.targetDoc, facetHeader, values)} values={renderInfoRange!} > + {railProps => } + + {({ handles, activeHandleID, getHandleProps }) => ( +
+ {handles.map((handle, i) => { + // const value = i === 0 ? defaultValues[0] : defaultValues[1]; + return ( +
+ +
+ ); + })} +
+ )} +
+ + {({ tracks, getTrackProps }) => ( +
+ {tracks.map(({ id, source, target }) => ( + + ))}
- ); - })} -
- )} - - - {({ tracks, getTrackProps }) => ( -
- {tracks.map(({ id, source, target }) => ( - - ))} -
- )} -
- - {({ ticks }) => ( -
- {ticks.map(tick => ( - val.toString()} /> - ))} -
- )} -
- + )} + + + {({ ticks }) => ( +
+ {ticks.map(tick => ( + val.toString()} /> + ))} +
+ )} +
+ +
+ ) } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index d07028ec2..84b1705bc 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1414,14 +1414,19 @@ export namespace Doc { prevLayout === 'icon' && (doc.deiconifyLayout = undefined); doc.layout_fieldKey = deiconify || 'layout'; } - export function setDocRangeFilter(container: Opt, key: string, range?: readonly number[]) { + export function setDocRangeFilter(container: Opt, key: string, range?: readonly number[], modifiers?: 'remove') { //, modifiers: 'remove' | 'set' if (!container) return; - + const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []); + + + + for (let i = 0; i < childFiltersByRanges.length; i += 3) { - console.log("inside if statement") if (childFiltersByRanges[i] === key) { + console.log("this is key inside childfilters by range " + key) childFiltersByRanges.splice(i, 3); + console.log("this is child filters by range " + childFiltersByRanges) break; } } @@ -1431,7 +1436,15 @@ export namespace Doc { childFiltersByRanges.push(range[0].toString()); childFiltersByRanges.push(range[1].toString()); container._childFiltersByRanges = new List(childFiltersByRanges); + console.log("this is child filters by range "+ childFiltersByRanges[0] + "," + childFiltersByRanges[1] + "," + childFiltersByRanges[2]) + console.log("this is new list " + container._childFiltersByRange) + } + + if (modifiers){ + childFiltersByRanges.splice(0,3) + container._childFiltersByRanges = new List(childFiltersByRanges); } + console.log("this is child filters by range END"+ childFiltersByRanges[0] + "," + childFiltersByRanges[1] + "," + childFiltersByRanges[2]) } export const FilterSep = '::'; -- cgit v1.2.3-70-g09d2 From 8a58d0205111e7a653c0a35acecb41790882cb7b Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 2 Aug 2023 16:40:03 -0400 Subject: Pre master --- src/client/views/nodes/MapBox/MapBox.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 61296c6c8..d5787db3d 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -654,6 +654,13 @@ export class MapBox extends ViewBoxAnnotatableComponent { + this.removePushpin(pinDoc,pin); + }; /* * Pushpin onclick */ @@ -666,9 +673,6 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(pin,pushPin)); + this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pin,pushPin)); } + @observable pinIsSelected_TEMPORARY:boolean=false; // toggles if remove pin button appears -- cgit v1.2.3-70-g09d2 From aa9f4b3ced3b4c6a1850cb9a0b3ea2f188998a55 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Wed, 2 Aug 2023 17:01:35 -0400 Subject: filters - sliders working (still fix UI & comments from meeting) --- src/client/views/FilterPanel.tsx | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 54f5122b4..8ecd60c7e 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -219,21 +219,40 @@ export class FilterPanel extends React.Component { @action sortingCurrentFacetValues = (facetHeader:string) => { - console.log("in function to begin with") + // console.log("in function to begin with") this._collapseReturnKeys.splice(0) - console.log("this si sfacetValies " + this.facetValues(facetHeader)) + // console.log("this si sfacetValies " + this.facetValues(facetHeader)) //if range then display range values + //use to -- & determine amount of sigfinict digits -- make all sections blue evn when collapsed + // transform switch to x and y not x coordinate and y coordinate + + Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => { + if ( renderInfo.renderType === "range" && renderInfo.facetHeader === facetHeader) { + console.log("THIS IS ONLY SHWOIGN UP ONCE ") + console.log("hope same thing "+ this.targetDoc?._childFiltersByRanges?.toString + " extra " + renderInfo.range) + this._collapseReturnKeys.push(renderInfo.range) + + } + + }) + + + for (var key of this.facetValues(facetHeader)){ + // console.log("this is key " + key) + if (this.mapActiveFiltersToFacets.get(key)){ + + this._collapseReturnKeys.push(key) + }} + + // if(Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => (renderInfo.renderType === "range" && renderInfo.facetHeader === facetHeader ))){ - // console.log("JOE MAMA") + // } - for (var key of this.facetValues(facetHeader)){ - if (this.mapActiveFiltersToFacets.get(key)){ - this._collapseReturnKeys.push(key) - }} + return (
{this._collapseReturnKeys.join(', ') } -- cgit v1.2.3-70-g09d2 From 93854ed0917346720ff519553f909cee3561ec7b Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Thu, 3 Aug 2023 12:31:03 -0400 Subject: filter - fixed bug --- src/client/views/FilterPanel.tsx | 44 +++++++++------------------------------- 1 file changed, 10 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 8ecd60c7e..54701b7f8 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -18,6 +18,9 @@ import { TooltipRail, Handle, Tick, Track } from './nodes/SliderBox-components'; //slight bug when you don't click on background canvas before creating filter and the you click on the canvas + //use to -- & determine amount of sigfinict digits -- make all sections blue evn when collapsed + // transform switch to x and y not x coordinate and y coordinate + interface filterProps { rootDoc: Doc; } @@ -219,40 +222,22 @@ export class FilterPanel extends React.Component { @action sortingCurrentFacetValues = (facetHeader:string) => { - // console.log("in function to begin with") this._collapseReturnKeys.splice(0) - // console.log("this si sfacetValies " + this.facetValues(facetHeader)) - - //if range then display range values - - //use to -- & determine amount of sigfinict digits -- make all sections blue evn when collapsed - // transform switch to x and y not x coordinate and y coordinate - Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => { if ( renderInfo.renderType === "range" && renderInfo.facetHeader === facetHeader) { console.log("THIS IS ONLY SHWOIGN UP ONCE ") console.log("hope same thing "+ this.targetDoc?._childFiltersByRanges?.toString + " extra " + renderInfo.range) this._collapseReturnKeys.push(renderInfo.range) - } - + } }) - for (var key of this.facetValues(facetHeader)){ - // console.log("this is key " + key) if (this.mapActiveFiltersToFacets.get(key)){ - this._collapseReturnKeys.push(key) }} - - // if(Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => (renderInfo.renderType === "range" && renderInfo.facetHeader === facetHeader ))){ - - // } - - return (
{this._collapseReturnKeys.join(', ') } @@ -315,36 +300,27 @@ export class FilterPanel extends React.Component {
{ - - const collapseBoolValue = this._chosenFacetsCollapse.get(renderInfo.facetHeader) this._chosenFacetsCollapse.set(renderInfo.facetHeader, !collapseBoolValue )})}> - {this._chosenFacetsCollapse.get(renderInfo.facetHeader) ? : }
{ + onClick = {action((e) => { + for (var key of this.facetValues(renderInfo.facetHeader)){ if (this.mapActiveFiltersToFacets.get(key)){ Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, key, 'remove') }} this._selectedFacetHeaders.delete(renderInfo.facetHeader) this._chosenFacetsCollapse.delete(renderInfo.facetHeader) - Doc.setDocRangeFilter(this.targetDoc, renderInfo.facetHeader, renderInfo.domain, 'remove') - - - - console.log("this is activeFilters " + this.activeFilters) - console.log("this is activeFacetHeaders " + this.activeFacetHeaders) - console.log("thsi is activeRenderedFacetInfos " + this.activeRenderedFacetInfos) - console.log("thsi is selected facet Headers " + this._selectedFacetHeaders ) - console.log("THIS IS THE ONE ADDED "+ this.targetDoc?._childFiltersByRanges) - - + if (renderInfo.domain){ + Doc.setDocRangeFilter(this.targetDoc, renderInfo.facetHeader, renderInfo.domain, 'remove') + } + })} >
-- cgit v1.2.3-70-g09d2 From 082a280ed989007d84c8619438889c64a6fedb14 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Fri, 4 Aug 2023 11:54:03 -0400 Subject: filters - starting to add filterable field --- src/client/documents/Documents.ts | 6 +++--- src/client/views/FilterPanel.tsx | 9 +-------- src/client/views/MainView.scss | 8 ++++---- src/client/views/MainView.tsx | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5d7ca6f6d..5d8ae19fc 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -88,7 +88,7 @@ class BoolInfo extends FInfo { class NumInfo extends FInfo { fieldType? = 'number'; values?: number[] = []; - constructor(d: string, readOnly?: boolean, values?: number[]) { + constructor(d: string, readOnly?: boolean, values?: number[], filterable?:boolean) { super(d, readOnly); this.values = values; } @@ -104,7 +104,7 @@ class StrInfo extends FInfo { class DocInfo extends FInfo { fieldType? = 'Doc'; values?: Doc[] = []; - constructor(d: string, values?: Doc[]) { + constructor(d: string, filterable?:boolean, values?: Doc[] ) { super(d, true); this.values = values; } @@ -183,7 +183,7 @@ export class DocumentOptions { caption?: RichTextField; author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable author_date?: DATEt = new DateInfo('date the document was created', true); - annotationOn?: DOCt = new DocInfo('document annotated by this document'); + annotationOn?: DOCt = new DocInfo('document annotated by this document', false); color?: STRt = new StrInfo('foreground color data doc'); hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection'); backgroundColor?: STRt = new StrInfo('background color for data doc'); diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 54701b7f8..127708ca3 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -226,8 +226,6 @@ export class FilterPanel extends React.Component { Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => { if ( renderInfo.renderType === "range" && renderInfo.facetHeader === facetHeader) { - console.log("THIS IS ONLY SHWOIGN UP ONCE ") - console.log("hope same thing "+ this.targetDoc?._childFiltersByRanges?.toString + " extra " + renderInfo.range) this._collapseReturnKeys.push(renderInfo.range) } @@ -316,16 +314,11 @@ export class FilterPanel extends React.Component { this._selectedFacetHeaders.delete(renderInfo.facetHeader) this._chosenFacetsCollapse.delete(renderInfo.facetHeader) - if (renderInfo.domain){ Doc.setDocRangeFilter(this.targetDoc, renderInfo.facetHeader, renderInfo.domain, 'remove') - } - - })} > + }})} >
- {/*
*/} -
{ this._chosenFacetsCollapse.get(renderInfo.facetHeader) ? diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index b3faff442..8a7f5132b 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -194,11 +194,11 @@ h1, left: 0; position: absolute; z-index: 2; - background-color: linen; //$light-gray; + // background-color: linen; //$light-gray; - .editable-title { - background-color: linen; //$light-gray; - } + // .editable-title { + // background-color: linen; //$light-gray; + // } } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cbaa763f5..d9136dbd4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -813,7 +813,7 @@ export class MainView extends React.Component { {this.dockingContent} {this._hideUI ? null : ( -
+
)} -- cgit v1.2.3-70-g09d2 From 1c5bb5390ab9f198acde7d48aaa7d7d536f432cd Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Thu, 10 Aug 2023 18:46:04 -0400 Subject: need to pull --- package-lock.json | 39 --- src/.DS_Store | Bin 10244 -> 10244 bytes src/client/documents/Documents.ts | 11 +- src/client/util/.ClientUtils.ts.icloud | Bin 0 -> 161 bytes src/client/util/.ReportManager.scss.icloud | Bin 0 -> 168 bytes src/client/util/.ReportManager.tsx.icloud | Bin 0 -> 167 bytes src/client/views/.Palette.scss.icloud | Bin 0 -> 160 bytes src/client/views/DashboardView.tsx | 78 +++--- src/client/views/FilterPanel.tsx | 371 ++++++++++++++------------- src/client/views/Palette.tsx | 69 +++++ src/client/views/nodes/.QueryBox.scss.icloud | Bin 0 -> 160 bytes src/client/views/nodes/.QueryBox.tsx.icloud | Bin 0 -> 160 bytes src/fields/.ListSpec.ts.icloud | Bin 0 -> 158 bytes src/fields/IconField.ts | 26 ++ src/fields/PresField.ts | 6 + src/mobile/MobileInterface.tsx | 1 + src/server/stats/.userLoginStats.csv.icloud | Bin 0 -> 168 bytes 17 files changed, 329 insertions(+), 272 deletions(-) create mode 100644 src/client/util/.ClientUtils.ts.icloud create mode 100644 src/client/util/.ReportManager.scss.icloud create mode 100644 src/client/util/.ReportManager.tsx.icloud create mode 100644 src/client/views/.Palette.scss.icloud create mode 100644 src/client/views/Palette.tsx create mode 100644 src/client/views/nodes/.QueryBox.scss.icloud create mode 100644 src/client/views/nodes/.QueryBox.tsx.icloud create mode 100644 src/fields/.ListSpec.ts.icloud create mode 100644 src/fields/IconField.ts create mode 100644 src/fields/PresField.ts create mode 100644 src/server/stats/.userLoginStats.csv.icloud (limited to 'src') diff --git a/package-lock.json b/package-lock.json index e08568816..884a64a2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10225,16 +10225,6 @@ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "d3": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz", @@ -11615,28 +11605,6 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "dev": true, - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", @@ -11648,7 +11616,6 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { - "d": "^1.0.1", "ext": "^1.1.2" } }, @@ -27407,12 +27374,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/src/.DS_Store b/src/.DS_Store index 06389d6ae..946e85928 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5d8ae19fc..533df5c11 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -74,6 +74,8 @@ export class FInfo { readOnly: boolean = false; fieldType?: string = ''; values?: Field[]; + + filterable?: boolean = true; // format?: string; // format to display values (e.g, decimal places, $, etc) // parse?: ScriptField; // parse a value from a string constructor(d: string, readOnly?: boolean) { @@ -88,7 +90,7 @@ class BoolInfo extends FInfo { class NumInfo extends FInfo { fieldType? = 'number'; values?: number[] = []; - constructor(d: string, readOnly?: boolean, values?: number[], filterable?:boolean) { + constructor(d: string, readOnly?: boolean, values?: number[], filterable?: boolean) { super(d, readOnly); this.values = values; } @@ -96,15 +98,16 @@ class NumInfo extends FInfo { class StrInfo extends FInfo { fieldType? = 'string'; values?: string[] = []; - constructor(d: string, readOnly?: boolean, values?: string[], filterable?:boolean) { + constructor(d: string, filterable?: boolean, readOnly?: boolean, values?: string[]) { super(d, readOnly); this.values = values; + this.filterable = filterable; } } class DocInfo extends FInfo { fieldType? = 'Doc'; values?: Doc[] = []; - constructor(d: string, filterable?:boolean, values?: Doc[] ) { + constructor(d: string, filterable?: boolean, values?: Doc[]) { super(d, true); this.values = values; } @@ -184,7 +187,7 @@ export class DocumentOptions { author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable author_date?: DATEt = new DateInfo('date the document was created', true); annotationOn?: DOCt = new DocInfo('document annotated by this document', false); - color?: STRt = new StrInfo('foreground color data doc'); + color?: STRt = new StrInfo('foreground color data doc', true); hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection'); backgroundColor?: STRt = new StrInfo('background color for data doc'); opacity?: NUMt = new NumInfo('document opacity'); diff --git a/src/client/util/.ClientUtils.ts.icloud b/src/client/util/.ClientUtils.ts.icloud new file mode 100644 index 000000000..e5e477586 Binary files /dev/null and b/src/client/util/.ClientUtils.ts.icloud differ diff --git a/src/client/util/.ReportManager.scss.icloud b/src/client/util/.ReportManager.scss.icloud new file mode 100644 index 000000000..f5d34d292 Binary files /dev/null and b/src/client/util/.ReportManager.scss.icloud differ diff --git a/src/client/util/.ReportManager.tsx.icloud b/src/client/util/.ReportManager.tsx.icloud new file mode 100644 index 000000000..72924c53a Binary files /dev/null and b/src/client/util/.ReportManager.tsx.icloud differ diff --git a/src/client/views/.Palette.scss.icloud b/src/client/views/.Palette.scss.icloud new file mode 100644 index 000000000..49a2ac2da Binary files /dev/null and b/src/client/views/.Palette.scss.icloud differ diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index ae55c8ebf..d6c7b43d5 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -43,7 +43,7 @@ export class DashboardView extends React.Component { @observable private selectedDashboardGroup = DashboardGroup.MyDashboards; @observable private newDashboardName: string | undefined = undefined; - @observable private newDashboardColor: string | undefined = "#AFAFAF"; + @observable private newDashboardColor: string | undefined = '#AFAFAF'; @action abortCreateNewDashboard = () => { this.newDashboardName = undefined; }; @@ -100,24 +100,17 @@ export class DashboardView extends React.Component { const dashboardCount = DocListCast(Doc.MyDashboards.data).length + 1; const placeholder = `Dashboard ${dashboardCount}`; return ( -
+ color: StrCast(Doc.UserDoc().userColor), + }}>
Create New Dashboard
- this.setNewDashboardName(val as string)} - fillWidth - /> + this.setNewDashboardName(val as string)} fillWidth /> { @@ -165,21 +158,14 @@ export class DashboardView extends React.Component { }; render() { - const color = StrCast(Doc.UserDoc().userColor) - const variant = StrCast(Doc.UserDoc().userVariantColor) + const color = StrCast(Doc.UserDoc().userColor); + const variant = StrCast(Doc.UserDoc().userVariantColor); return ( <>
- + + +
+
+

{this.questionPartOne}

+

{this.questionPartTwo}

+
+
+ {this.selectedQuestion.answerParts.includes('force of gravity') && ( + Gravity magnitude

} + lowerBound={0} + dataDoc={this.dataDoc} + prop="review_GravityMagnitude" + step={0.1} + unit={'N'} + upperBound={50} + value={NumCast(this.dataDoc.review_GravityMagnitude)} + showIcon={BoolCast(this.dataDoc.simulation_showIcon)} + correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('force of gravity')]} + labelWidth={'7em'} + /> + )} + {this.selectedQuestion.answerParts.includes('angle of gravity') && ( + Gravity angle

} + lowerBound={0} + dataDoc={this.dataDoc} + prop="review_GravityAngle" + step={1} + unit={'°'} + upperBound={360} + value={NumCast(this.dataDoc.review_GravityAngle)} + radianEquivalent={true} + showIcon={BoolCast(this.dataDoc.simulation_showIcon)} + correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of gravity')]} + labelWidth={'7em'} + /> + )} + {this.selectedQuestion.answerParts.includes('normal force') && ( + Normal force magnitude

} + lowerBound={0} + dataDoc={this.dataDoc} + prop="review_NormalMagnitude" + step={0.1} + unit={'N'} + upperBound={50} + value={NumCast(this.dataDoc.review_NormalMagnitude)} + showIcon={BoolCast(this.dataDoc.simulation_showIcon)} + correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('normal force')]} + labelWidth={'7em'} + /> + )} + {this.selectedQuestion.answerParts.includes('angle of normal force') && ( + Normal force angle

} + lowerBound={0} + dataDoc={this.dataDoc} + prop="review_NormalAngle" + step={1} + unit={'°'} + upperBound={360} + value={NumCast(this.dataDoc.review_NormalAngle)} + radianEquivalent={true} + showIcon={BoolCast(this.dataDoc.simulation_showIcon)} + correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of normal force')]} + labelWidth={'7em'} + /> + )} + {this.selectedQuestion.answerParts.includes('force of static friction') && ( + Static friction magnitude

} + lowerBound={0} + dataDoc={this.dataDoc} + prop="review_StaticMagnitude" + step={0.1} + unit={'N'} + upperBound={50} + value={NumCast(this.dataDoc.review_StaticMagnitude)} + showIcon={BoolCast(this.dataDoc.simulation_showIcon)} + correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('force of static friction')]} + labelWidth={'7em'} + /> + )} + {this.selectedQuestion.answerParts.includes('angle of static friction') && ( + Static friction angle

} + lowerBound={0} + dataDoc={this.dataDoc} + prop="review_StaticAngle" + step={1} + unit={'°'} + upperBound={360} + value={NumCast(this.dataDoc.review_StaticAngle)} + radianEquivalent={true} + showIcon={BoolCast(this.dataDoc.simulation_showIcon)} + correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of static friction')]} + labelWidth={'7em'} + /> + )} + {this.selectedQuestion.answerParts.includes('coefficient of static friction') && ( + + μs + + } + lowerBound={0} + dataDoc={this.dataDoc} + prop="coefficientOfStaticFriction" + step={0.1} + unit={''} + upperBound={1} + value={NumCast(this.dataDoc.coefficientOfStaticFriction)} + effect={this.updateReviewForcesBasedOnCoefficient} + showIcon={BoolCast(this.dataDoc.simulation_showIcon)} + correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('coefficient of static friction')]} + /> + )} + {this.selectedQuestion.answerParts.includes('wedge angle') && ( + θ} + lowerBound={0} + dataDoc={this.dataDoc} + prop="wedge_angle" + step={1} + unit={'°'} + upperBound={49} + value={this.wedgeAngle} + effect={(val: number) => { + this.changeWedgeBasedOnNewAngle(val); + this.updateReviewForcesBasedOnAngle(val); + }} + radianEquivalent={true} + showIcon={BoolCast(this.dataDoc.simulation_showIcon)} + correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('wedge angle')]} + /> + )} +
+
+
+ )} + {this.simulationMode == 'Tutorial' && ( +
+
+

Problem

+

{this.tutorial.question}

+
+
+ { + let step = NumCast(this.dataDoc.tutorial_stepNumber) - 1; + step = Math.max(step, 0); + step = Math.min(step, this.tutorial.steps.length - 1); + this.dataDoc.tutorial_stepNumber = step; + this.dataDoc.mass1_forcesStart = JSON.stringify(this.tutorial.steps[step].forces); + this.dataDoc.mass1_forcesUpdated = JSON.stringify(this.tutorial.steps[step].forces); + this.dataDoc.simulation_showForceMagnitudes = this.tutorial.steps[step].showMagnitude; + }} + disabled={this.dataDoc.tutorial_stepNumber == 0}> + {/* */} + +
+

+ Step {NumCast(this.dataDoc.tutorial_stepNumber) + 1}: {this.tutorial.steps[NumCast(this.dataDoc.tutorial_stepNumber)].description} +

+

{this.tutorial.steps[NumCast(this.dataDoc.tutorial_stepNumber)].content}

+
+ { + let step = NumCast(this.dataDoc.tutorial_stepNumber) + 1; + step = Math.max(step, 0); + step = Math.min(step, this.tutorial.steps.length - 1); + this.dataDoc.tutorial_stepNumber = step; + this.dataDoc.mass1_forcesStart = JSON.stringify(this.tutorial.steps[step].forces); + this.dataDoc.mass1_forcesUpdated = JSON.stringify(this.tutorial.steps[step].forces); + this.dataDoc.simulation_showForceMagnitudes = this.tutorial.steps[step].showMagnitude; + }} + disabled={this.dataDoc.tutorial_stepNumber === this.tutorial.steps.length - 1}> + {/* */} + +
+
+ {(this.simulationType == 'One Weight' || this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') &&

Resources

} + {this.simulationType == 'One Weight' && ( + + )} + {this.simulationType == 'Inclined Plane' && ( + + )} + {this.simulationType == 'Pendulum' && ( + + )} +
+
+ )} + {this.simulationMode == 'Review' && this.simulationType == 'Inclined Plane' && ( +
+

(this.dataDoc.simulation_mode = 'Tutorial')}> + {' '} + Go to walkthrough{' '} +

+
+ + +
+
+ )} + {this.simulationMode == 'Freeform' && ( +
+ + + {this.simulationType == 'One Weight' && ( + (this.dataDoc.elasticCollisions = !this.dataDoc.elasticCollisions)} />} + label="Make collisions elastic" + labelPlacement="start" + /> + )} + (this.dataDoc.simulation_showForces = !this.dataDoc.simulation_showForces)} />} + label="Show force vectors" + labelPlacement="start" + /> + {(this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') && ( + (this.dataDoc.simulation_showComponentForces = !this.dataDoc.simulation_showComponentForces)} />} + label="Show component force vectors" + labelPlacement="start" + /> + )} + (this.dataDoc.simulation_showAcceleration = !this.dataDoc.simulation_showAcceleration)} />} + label="Show acceleration vector" + labelPlacement="start" + /> + (this.dataDoc.simulation_showVelocity = !this.dataDoc.simulation_showVelocity)} />} + label="Show velocity vector" + labelPlacement="start" + /> + Speed} lowerBound={1} dataDoc={this.dataDoc} prop="simulation_speed" step={1} unit={'x'} upperBound={10} value={NumCast(this.dataDoc.simulation_speed, 2)} labelWidth={'5em'} /> + {this.dataDoc.simulation_paused && this.simulationType != 'Circular Motion' && ( + Gravity} + lowerBound={-30} + dataDoc={this.dataDoc} + prop="gravity" + step={0.01} + unit={'m/s2'} + upperBound={0} + value={NumCast(this.dataDoc.simulation_gravity, -9.81)} + effect={(val: number) => this.setupSimulation()} + labelWidth={'5em'} + /> + )} + {this.dataDoc.simulation_paused && this.simulationType != 'Pulley' && ( + Mass} + lowerBound={1} + dataDoc={this.dataDoc} + prop="mass1" + step={0.1} + unit={'kg'} + upperBound={5} + value={this.mass1 ?? 1} + effect={(val: number) => this.setupSimulation()} + labelWidth={'5em'} + /> + )} + {this.dataDoc.simulation_paused && this.simulationType == 'Pulley' && ( + Red mass} + lowerBound={1} + dataDoc={this.dataDoc} + prop="mass1" + step={0.1} + unit={'kg'} + upperBound={5} + value={this.mass1 ?? 1} + effect={(val: number) => this.setupSimulation()} + labelWidth={'5em'} + /> + )} + {this.dataDoc.simulation_paused && this.simulationType == 'Pulley' && ( + Blue mass} + lowerBound={1} + dataDoc={this.dataDoc} + prop="mass2" + step={0.1} + unit={'kg'} + upperBound={5} + value={this.mass2 ?? 1} + effect={(val: number) => this.setupSimulation()} + labelWidth={'5em'} + /> + )} + {this.dataDoc.simulation_paused && this.simulationType == 'Circular Motion' && ( + Rod length} + lowerBound={100} + dataDoc={this.dataDoc} + prop="circularMotionRadius" + step={5} + unit={'m'} + upperBound={250} + value={this.circularMotionRadius} + effect={(val: number) => this.setupSimulation()} + labelWidth={'5em'} + /> + )} + + + {this.simulationType == 'Spring' && this.dataDoc.simulation_paused && ( +
+ Spring stiffness} + lowerBound={0.1} + dataDoc={this.dataDoc} + prop="spring_constant" + step={1} + unit={'N/m'} + upperBound={500} + value={this.springConstant} + effect={action(() => this._simReset++)} + radianEquivalent={false} + mode={'Freeform'} + labelWidth={'7em'} + /> + Rest length} + lowerBound={10} + dataDoc={this.dataDoc} + prop="spring_lengthRest" + step={100} + unit="" + upperBound={500} + value={this.springLengthRest} + effect={action(() => this._simReset++)} + radianEquivalent={false} + mode="Freeform" + labelWidth={'7em'} + /> + Starting displacement} + lowerBound={-(this.springLengthRest - 10)} + dataDoc={this.dataDoc} + prop="" + step={10} + unit="" + upperBound={this.springLengthRest} + value={this.springLengthStart - this.springLengthRest} + effect={action((val: number) => { + this.dataDoc.mass1_positionYstart = this.springLengthRest + val; + this.dataDoc.spring_lengthStart = this.springLengthRest + val; + this._simReset++; + })} + radianEquivalent={false} + mode="Freeform" + labelWidth={'7em'} + /> +
+ )} + {this.simulationType == 'Inclined Plane' && this.dataDoc.simulation_paused && ( +
+ θ} + lowerBound={0} + dataDoc={this.dataDoc} + prop="wedge_angle" + step={1} + unit={'°'} + upperBound={49} + value={this.wedgeAngle} + effect={action((val: number) => { + this.changeWedgeBasedOnNewAngle(val); + this._simReset++; + })} + radianEquivalent={true} + mode={'Freeform'} + labelWidth={'2em'} + /> + + μs + + } + lowerBound={0} + dataDoc={this.dataDoc} + prop="coefficientOfStaticFriction" + step={0.1} + unit={''} + upperBound={1} + value={NumCast(this.dataDoc.coefficientOfStaticFriction) ?? 0} + effect={action((val: number) => { + this.updateForcesWithFriction(val); + if (val < NumCast(this.dataDoc.coefficientOfKineticFriction)) { + this.dataDoc.soefficientOfKineticFriction = val; + } + this._simReset++; + })} + mode={'Freeform'} + labelWidth={'2em'} + /> + + μk + + } + lowerBound={0} + dataDoc={this.dataDoc} + prop="coefficientOfKineticFriction" + step={0.1} + unit={''} + upperBound={NumCast(this.dataDoc.coefficientOfStaticFriction)} + value={NumCast(this.dataDoc.coefficientOfKineticFriction) ?? 0} + effect={action(() => this._simReset++)} + mode={'Freeform'} + labelWidth={'2em'} + /> +
+ )} + {this.simulationType == 'Inclined Plane' && !this.dataDoc.simulation_paused && ( + + <> + θ: {Math.round(this.wedgeAngle * 100) / 100}° ≈ {Math.round(((this.wedgeAngle * Math.PI) / 180) * 100) / 100} rad +
+ μ s: {this.dataDoc.coefficientOfStaticFriction} +
+ μ k: {this.dataDoc.coefficientOfKineticFriction} + +
+ )} + {this.simulationType == 'Pendulum' && !this.dataDoc.simulation_paused && ( + + θ: {Math.round(this.pendulumAngle * 100) / 100}° ≈ {Math.round(((this.pendulumAngle * Math.PI) / 180) * 100) / 100} rad + + )} + {this.simulationType == 'Pendulum' && this.dataDoc.simulation_paused && ( +
+ Angle} + lowerBound={0} + dataDoc={this.dataDoc} + prop="pendulum_angle" + step={1} + unit={'°'} + upperBound={59} + value={NumCast(this.dataDoc.pendulum_angle, 30)} + effect={action(value => { + this.dataDoc.pendulum_angleStart = value; + this.dataDoc.pendulum_lengthStart = this.dataDoc.pendulum_length; + if (this.simulationType == 'Pendulum') { + const mag = this.mass1 * Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180); + + const forceOfTension: IForce = { + description: 'Tension', + magnitude: mag, + directionInDegrees: 90 - value, + }; + const gravityParallel: IForce = { + description: 'Gravity Parallel Component', + magnitude: Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180), + directionInDegrees: 270 - value, + }; + const gravityPerpendicular: IForce = { + description: 'Gravity Perpendicular Component', + magnitude: Math.abs(this.gravity) * Math.sin((value * Math.PI) / 180), + directionInDegrees: -value, + }; + + const length = this.pendulumLength; + const x = length * Math.cos(((90 - value) * Math.PI) / 180); + const y = length * Math.sin(((90 - value) * Math.PI) / 180); + const xPos = this.xMax / 2 - x - NumCast(this.dataDoc.radius); + const yPos = y - NumCast(this.dataDoc.radius) - 5; + this.dataDoc.mass1_positionXstart = xPos; + this.dataDoc.mass1_positionYstart = yPos; + + this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]); + this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]); + this.dataDoc.mass1_componentForces = JSON.stringify([forceOfTension, gravityParallel, gravityPerpendicular]); + this._simReset++; + } + })} + radianEquivalent={true} + mode="Freeform" + labelWidth="5em" + /> + Rod length} + lowerBound={0} + dataDoc={this.dataDoc} + prop="pendulum_length" + step={1} + unit="m" + upperBound={400} + value={Math.round(this.pendulumLength)} + effect={action(value => { + if (this.simulationType == 'Pendulum') { + this.dataDoc.pendulum_angleStart = this.pendulumAngle; + this.dataDoc.pendulum_lengthStart = value; + this._simReset++; + } + })} + radianEquivalent={false} + mode="Freeform" + labelWidth="5em" + /> +
+ )} +
+ )} +
+ {this.simulationMode == 'Freeform' && ( + + + + + + + + + + {(!this.dataDoc.simulation_paused || this.simulationType == 'Inclined Plane' || this.simulationType == 'Circular Motion' || this.simulationType == 'Pulley') && ( + + )}{' '} + {this.dataDoc.simulation_paused && this.simulationType != 'Inclined Plane' && this.simulationType != 'Circular Motion' && this.simulationType != 'Pulley' && ( + + )}{' '} + {(!this.dataDoc.simulation_paused || this.simulationType == 'Inclined Plane' || this.simulationType == 'Circular Motion' || this.simulationType == 'Pulley') && ( + + )}{' '} + {this.dataDoc.simulation_paused && this.simulationType != 'Inclined Plane' && this.simulationType != 'Circular Motion' && this.simulationType != 'Pulley' && ( + + )}{' '} + + + + {(!this.dataDoc.simulation_paused || (this.simulationType != 'One Weight' && this.simulationType != 'Circular Motion')) && ( + + )}{' '} + {this.dataDoc.simulation_paused && (this.simulationType == 'One Weight' || this.simulationType == 'Circular Motion') && ( + + )}{' '} + {(!this.dataDoc.simulation_paused || this.simulationType != 'One Weight') && ( + + )}{' '} + {this.dataDoc.simulation_paused && this.simulationType == 'One Weight' && ( + + )}{' '} + + + + + + + + + + + + +
{this.simulationType == 'Pulley' ? 'Red Weight' : ''}XY
{ + // window.open( + // "https://www.khanacademy.org/science/physics/two-dimensional-motion" + // ); + // }} + > + Position + + <>{this.dataDoc.mass1_positionX} m + + { + this.dataDoc.mass1_xChange = value; + if (this.simulationType == 'Suspension') { + let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; + let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; + let deltaX1 = value + this.radius - x1rod; + let deltaX2 = x2rod - (value + this.radius); + let deltaY = this.getYPosFromDisplay(NumCast(this.dataDoc.mass1_positionY)) + this.radius; + let dir1T = Math.PI - Math.atan(deltaY / deltaX1); + let dir2T = Math.atan(deltaY / deltaX2); + let tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); + let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); + dir1T = (dir1T * 180) / Math.PI; + dir2T = (dir2T * 180) / Math.PI; + const tensionForce1: IForce = { + description: 'Tension', + magnitude: tensionMag1, + directionInDegrees: dir1T, + }; + const tensionForce2: IForce = { + description: 'Tension', + magnitude: tensionMag2, + directionInDegrees: dir2T, + }; + const gravity = this.gravityForce(this.mass1); + this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]); + } + }} + small={true} + mode="Freeform" + /> + {`${NumCast(this.dataDoc.mass1_positionY)} m`} + { + this.dataDoc.mass1_yChange = value; + if (this.simulationType == 'Suspension') { + let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; + let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; + let deltaX1 = NumCast(this.dataDoc.mass1_positionX) + this.radius - x1rod; + let deltaX2 = x2rod - (NumCast(this.dataDoc.mass1_positionX) + this.radius); + let deltaY = this.getYPosFromDisplay(value) + this.radius; + let dir1T = Math.PI - Math.atan(deltaY / deltaX1); + let dir2T = Math.atan(deltaY / deltaX2); + let tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); + let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); + dir1T = (dir1T * 180) / Math.PI; + dir2T = (dir2T * 180) / Math.PI; + const tensionForce1: IForce = { + description: 'Tension', + magnitude: tensionMag1, + directionInDegrees: dir1T, + }; + const tensionForce2: IForce = { + description: 'Tension', + magnitude: tensionMag2, + directionInDegrees: dir2T, + }; + const gravity = this.gravityForce(this.mass1); + this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]); + } + }} + small={true} + mode="Freeform" + /> +
{ + // window.open( + // "https://www.khanacademy.org/science/physics/two-dimensional-motion" + // ); + // }} + > + Velocity + {`${NumCast(this.dataDoc.mass1_velocityX)} m/s`} + { + this.dataDoc.mass1_velocityXstart = value; + this._simReset++; + })} + small={true} + mode="Freeform" + /> + + <>{this.dataDoc.mass1_velocityY} m/s + + { + this.dataDoc.mass1_velocityYstart = -value; + }} + small={true} + mode="Freeform" + /> +
{ + // window.open( + // "https://www.khanacademy.org/science/physics/two-dimensional-motion" + // ); + // }} + > + Acceleration + + <> + {this.dataDoc.mass1_accelerationX} m/s2 + + + <> + {this.dataDoc.mass1_accelerationY} m/s2 + +
+ Momentum + {Math.round(NumCast(this.dataDoc.mass1_velocityX) * this.mass1 * 10) / 10} kg*m/s{Math.round(NumCast(this.dataDoc.mass1_velocityY) * this.mass1 * 10) / 10} kg*m/s
+ )} + {this.simulationMode == 'Freeform' && this.simulationType == 'Pulley' && ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Blue WeightXY
+ Position + {`${this.dataDoc.mass2_positionX} m`}{`${this.dataDoc.mass2_positionY} m`}
+ Velocity + {`${this.dataDoc.mass2_positionX} m/s`}{`${this.dataDoc.mass2_positionY} m/s`}
+ Acceleration + + <> + {this.dataDoc.mass2_accelerationX} m/s2 + + + <> + {this.dataDoc.mass2_accelerationY} m/s2 + +
+ Momentum + {Math.round(NumCast(this.dataDoc.mass2_velocityX) * this.mass1 * 10) / 10} kg*m/s{Math.round(NumCast(this.dataDoc.mass2_velocityY) * this.mass1 * 10) / 10} kg*m/s
+ )} +
+ {this.simulationType != 'Pendulum' && this.simulationType != 'Spring' && ( +
+

Kinematic Equations

+
    +
  • + Position: x1=x0+v0t+ + 1⁄ + 2at + 2 +
  • +
  • + Velocity: v1=v0+at +
  • +
  • Acceleration: a = F/m
  • +
+
+ )} + {this.simulationType == 'Spring' && ( +
+

Harmonic Motion Equations: Spring

+
    +
  • + Spring force: Fs=kd +
  • +
  • + Spring period: Ts=2π√m⁄ + k +
  • +
  • Equilibrium displacement for vertical spring: d = mg/k
  • +
  • + Elastic potential energy: Us=1⁄ + 2kd2 +
  • +
      +
    • Maximum when system is at maximum displacement, 0 when system is at 0 displacement
    • +
    +
  • + Translational kinetic energy: K=1⁄ + 2mv2 +
  • +
      +
    • Maximum when system is at maximum/minimum velocity (at 0 displacement), 0 when velocity is 0 (at maximum displacement)
    • +
    +
+
+ )} + {this.simulationType == 'Pendulum' && ( +
+

Harmonic Motion Equations: Pendulum

+
    +
  • + Pendulum period: Tp=2π√l⁄ + g +
  • +
+
+ )} +
+
+
+ + + + + + + + + +

+ {this.simulationType == 'Circular Motion' ? 'Z' : 'Y'} +

+

+ X +

+
+
+ ); + } +} diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx new file mode 100644 index 000000000..d595a499e --- /dev/null +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx @@ -0,0 +1,179 @@ +import { TextField, InputAdornment } from '@mui/material'; +import { Doc } from '../../../../fields/Doc'; +import React = require('react'); +import TaskAltIcon from '@mui/icons-material/TaskAlt'; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; +import { isNumber } from 'lodash'; +export interface IInputProps { + label?: JSX.Element; + lowerBound: number; + dataDoc: Doc; + prop: string; + step: number; + unit: string; + upperBound: number; + value: number | string | Array; + correctValue?: number; + showIcon?: boolean; + effect?: (val: number) => any; + radianEquivalent?: boolean; + small?: boolean; + mode?: string; + update?: boolean; + labelWidth?: string; +} + +interface IState { + tempValue: string | number | (string | number)[]; + tempRadianValue: number; + width: string; + margin: string; +} + +export default class InputField extends React.Component { + constructor(props: any) { + super(props); + this.state = { + tempValue: this.props.mode != 'Freeform' && !this.props.showIcon ? 0 : this.props.value, + tempRadianValue: this.props.mode != 'Freeform' && !this.props.showIcon ? 0 : (Number(this.props.value) * Math.PI) / 180, + width: this.props.small ? '6em' : '7em', + margin: this.props.small ? '0px' : '10px', + }; + } + + epsilon: number = 0.01; + + componentDidMount(): void { + this.setState({ tempValue: Number(this.props.value) }); + } + + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { + if (prevProps.value != this.props.value && isNumber(this.props.value) && !isNaN(this.props.value)) { + if (this.props.mode == 'Freeform') { + if (isNumber(this.state.tempValue) && Math.abs(this.state.tempValue - Number(this.props.value)) > 1) { + this.setState({ tempValue: Number(this.props.value) }); + } + } + } + if (prevProps.update != this.props.update) { + this.externalUpdate(); + } + } + + externalUpdate = () => { + this.setState({ tempValue: Number(this.props.value) }); + this.setState({ tempRadianValue: (Number(this.props.value) * Math.PI) / 180 }); + }; + + onChange = (event: React.ChangeEvent) => { + let value = event.target.value == '' ? 0 : Number(event.target.value); + if (value > this.props.upperBound) { + value = this.props.upperBound; + } else if (value < this.props.lowerBound) { + value = this.props.lowerBound; + } + if (this.props.prop != '') { + this.props.dataDoc[this.props.prop] = value; + } + this.setState({ tempValue: event.target.value == '' ? event.target.value : value }); + this.setState({ tempRadianValue: (value * Math.PI) / 180 }); + if (this.props.effect) { + this.props.effect(value); + } + }; + + onChangeRadianValue = (event: React.ChangeEvent) => { + let value = event.target.value === '' ? 0 : Number(event.target.value); + if (value > 2 * Math.PI) { + value = 2 * Math.PI; + } else if (value < 0) { + value = 0; + } + if (this.props.prop != '') { + this.props.dataDoc[this.props.prop] = (value * 180) / Math.PI; + } + this.setState({ tempValue: (value * 180) / Math.PI }); + this.setState({ tempRadianValue: value }); + if (this.props.effect) { + this.props.effect((value * 180) / Math.PI); + } + }; + + render() { + return ( +
+ {this.props.label && ( +
+ {this.props.label} +
+ )} + + {Math.abs(Number(this.props.value) - (this.props.correctValue ?? 0)) < this.epsilon && this.props.showIcon && } + {Math.abs(Number(this.props.value) - (this.props.correctValue ?? 0)) >= this.epsilon && this.props.showIcon && } + + ), + endAdornment: {this.props.unit}, + }} + /> + {this.props.radianEquivalent && ( +
+

+
+ )} + {this.props.radianEquivalent && ( + rad, + }} + /> + )} +
+ ); + } +} diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationQuestions.json b/src/client/views/nodes/PhysicsBox/PhysicsSimulationQuestions.json new file mode 100644 index 000000000..cc79f7aad --- /dev/null +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationQuestions.json @@ -0,0 +1,161 @@ +{ + "inclinePlane": [ + { + "questionSetup": [ + "There is a 1kg weight on an inclined plane. The plane is at a ", + " angle from the ground. The system is in equilibrium (the net force on the weight is 0)." + ], + "variablesForQuestionSetup": ["theta - max 45"], + "question": "What are the magnitudes and directions of the forces acting on the weight?", + "answerParts": [ + "force of gravity", + "angle of gravity", + "normal force", + "angle of normal force", + "force of static friction", + "angle of static friction" + ], + "answerSolutionDescriptions": [ + "9.81", + "270", + "solve normal force magnitude from wedge angle", + "solve normal force angle from wedge angle", + "solve static force magnitude from wedge angle given equilibrium", + "solve static force angle from wedge angle given equilibrium" + ], + "goal": "noMovement", + "hints": [ + { + "description": "Direction of Force of Gravity", + "content": "The force of gravity acts in the negative y direction: 3π/2 rad." + }, + { + "description": "Direction of Normal Force", + "content": "The normal force acts in the direction perpendicular to the incline plane: π/2-θ rad, where θ is the angle of the incline plane." + }, + { + "description": "Direction of Force of Friction", + "content": "The force of friction acts in the direction along the incline plane: π-θ rad, where θ is the angle of the incline plane." + }, + { + "description": "Magnitude of Force of Gravity", + "content": "The magnitude of the force of gravity is approximately 9.81." + }, + { + "description": "Magnitude of Normal Force", + "content": "The magnitude of the normal force is equal to m*g*cos(θ), where θ is the angle of the incline plane." + }, + { + "description": "Net Force in Equilibrium", + "content": "For the system to be in equilibrium, the sum of the x components of all forces must equal 0, and the sum of the y components of all forces must equal 0." + }, + { + "description": "X Component of Normal Force", + "content": "The x component of the normal force is equal to m*g*cos(θ)*cos(π/2-θ), where θ is the angle of the incline plane." + }, + { + "description": "X Component of Force of Friction", + "content": "Since the net force in the x direction must be 0, we know the magnitude of the x component of the friction force is m*g*cos(θ)*cos(π/2-θ)." + }, + { + "description": "Y Component of Normal Force", + "content": "The y component of the normal force is equal to m*g*cos(θ)*sin(π/2-θ), where θ is the angle of the incline plane. The y component of gravity is equal to m*g" + }, + { + "description": "Y Component of Force of Friction", + "content": "Since the net force in the x direction must be 0, we know the magnitude of the y component of the friction force is m*g-m*g*cos(θ)*sin(π/2-θ)." + }, + { + "description": "Magnitude of Force of Friction", + "content": "Combining the x and y components of the friction force, we get the magnitude of the friction force is equal to sqrt((m*g*cos(θ)*cos(π/2-θ))^2 + (m*g-m*g*cos(θ)*sin(π/2-θ))^2)." + } + ] + }, + { + "questionSetup": [ + "There is a 1kg weight on an inclined plane. The plane is at a ", + " angle from the ground. The system is in equilibrium (the net force on the weight is 0)." + ], + "variablesForQuestionSetup": ["theta - max 45"], + "question": "What is the minimum coefficient of static friction?", + "answerParts": ["coefficient of static friction"], + "answerSolutionDescriptions": [ + "solve minimum static coefficient from wedge angle given equilibrium" + ], + "goal": "noMovement", + "hints": [ + { + "description": "Net Force in Equilibrium", + "content": "If the system is in equilibrium, the sum of the x components of all forces must equal 0. In this system, the normal force and force of static friction have non-zero x components." + }, + { + "description": "X Component of Normal Force", + "content": "The x component of the normal force is equal to m*g*cos(θ)*cos(π/2-θ), where θ is the angle of the incline plane." + }, + { + "description": "X Component of Force of Friction", + "content": "The x component of the force of static friction is equal to μ*m*g*cos(θ)*cos(π-θ), where θ is the angle of the incline plane." + }, + { + "description": "Equation to Solve for Minimum Coefficient of Static Friction", + "content": "Since the net force in the x direction must be 0, we can solve the equation 0=m*g*cos(θ)*cos(π/2-θ)+μ*m*g*cos(θ)*cos(π-θ) for μ to find the minimum coefficient of static friction such that the system stays in equilibrium." + } + ] + }, + { + "questionSetup": [ + "There is a 1kg weight on an inclined plane. The coefficient of static friction is ", + ". The system is in equilibrium (the net force on the weight is 0)." + ], + "variablesForQuestionSetup": ["coefficient of static friction"], + "question": "What is the maximum angle of the plane from the ground?", + "answerParts": ["wedge angle"], + "answerSolutionDescriptions": [ + "solve maximum wedge angle from coefficient of static friction given equilibrium" + ], + "goal": "noMovement", + "hints": [ + { + "description": "Net Force in Equilibrium", + "content": "If the system is in equilibrium, the sum of the x components of all forces must equal 0. In this system, the normal force and force of static friction have non-zero x components." + }, + { + "description": "X Component of Normal Force", + "content": "The x component of the normal force is equal to m*g*cos(θ)*cos(π/2-θ), where θ is the angle of the incline plane." + }, + { + "description": "X Component of Force of Friction", + "content": "The x component of the force of static friction is equal to μ*m*g*cos(θ)*cos(π-θ), where θ is the angle of the incline plane." + }, + { + "description": "Equation to Solve for Maximum Wedge Angle", + "content": "Since the net force in the x direction must be 0, we can solve the equation 0=m*g*cos(θ)*cos(π/2-θ)+μ*m*g*cos(θ)*cos(π-θ) for θ to find the maximum wedge angle such that the system stays in equilibrium." + }, + { + "description": "Simplifying Equation to Solve for Maximum Wedge Angle", + "content": "Simplifying 0=m*g*cos(θ)*cos(π/2-θ)+μ*m*g*cos(θ)*cos(π-θ), we get cos(π/2-θ)=-μ*cos(π-θ)." + }, + { + "description": "Simplifying Equation to Solve for Maximum Wedge Angle", + "content": "The cosine subtraction formula states that cos(A-B)=cos(A)*cos(B)+sin(A)sin(B)." + }, + { + "description": "Simplifying Equation to Solve for Maximum Wedge Angle", + "content": "Applying the cosine subtraction formula to cos(π/2-θ)=-μ*cos(π-θ), we get cos(π/2)*cos(θ)+sin(π/2)*sin(θ)=-μ*(cos(π)cos(θ)+sin(π)sin(θ))." + }, + { + "description": "Simplifying Equation to Solve for Maximum Wedge Angle", + "content": "Simplifying cos(π/2)*cos(θ)-sin(π/2)*sin(θ)=-μ*(cos(π)cos(θ)-sin(π)sin(θ)), we get -sin(θ)=-μ*(-cos(θ))." + }, + { + "description": "Simplifying Equation to Solve for Maximum Wedge Angle", + "content": "Simplifying -sin(θ)=-μ*(-cos(θ)), we get tan(θ)=-μ." + }, + { + "description": "Simplifying Equation to Solve for Maximum Wedge Angle", + "content": "Solving for θ, we get θ = atan(μ)." + } + ] + } + ] +} diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationTutorial.json b/src/client/views/nodes/PhysicsBox/PhysicsSimulationTutorial.json new file mode 100644 index 000000000..3015deaa4 --- /dev/null +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationTutorial.json @@ -0,0 +1,600 @@ +{ + "freeWeight": { + "question": "A 1kg weight is at rest on the ground. What are the magnitude and directions of the forces acting on the weight?", + "steps": [ + { + "description": "Forces", + "content": "There are two forces acting on the weight: the force of gravity and the normal force.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Normal Force", + "magnitude": 9.81, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": false + }, + { + "description": "Force of Gravity", + "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "Normal Force", + "content": "The normal force acts in the positive y direction: π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.", + "forces": [ + { + "description": "Normal Force", + "magnitude": 9.81, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "All Forces", + "content": "Combining all of the forces, we get the following free body diagram.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Normal Force", + "magnitude": 9.81, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": true + } + ] + }, + "pendulum": { + "question": "A 1kg weight on a 300m rod of negligible mass is released from an angle 30 degrees offset from equilibrium. What are the magnitude and directions of the forces acting on the weight immediately after release? (Ignore air resistance)", + "steps": [ + { + "description": "Forces", + "content": "There are two force acting on the weight: the force of gravity and the force of tension.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Tension", + "magnitude": 8.5, + "directionInDegrees": 60, + "component": false + } + ], + "showMagnitude": false + }, + { + "description": "Force of Gravity", + "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "Tension", + "content": "The force of tension acts along the direction of the rod. The rod is 30 degrees offset from equilibrium, so the direction along the rod is 90-30=60 degrees. The tension force has two components—the component creating the centripetal force and the component canceling out the parallel component of gravity. The weight has just been released, so it has velocity 0, meaning the centripetal force is 0. Thus, the tension force only acts to cancel out the parallel component of gravity. Thus, the magnitude of tension is m*g*sin(60°)", + "forces": [ + { + "description": "Tension", + "magnitude": 8.5, + "directionInDegrees": 60, + "component": false + }, + { + "description": "Gravity - Parallel Component", + "magnitude": 8.5, + "directionInDegrees": 240, + "component": true + } + ], + "showMagnitude": true + }, + { + "description": "All Forces", + "content": "Combining all of the forces, we get the following free body diagram.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Tension", + "magnitude": 8.5, + "directionInDegrees": 60, + "component": false + } + ], + "showMagnitude": true + } + ] + }, + "inclinePlane": { + "question": "There is a 1kg weight on an inclined plane. The plane is at an angle θ from the ground, and has a coefficient of static friction μ. The system is in equilibrium (the net force on the weight is 0). What are the magnitudes and directions of the forces acting on the weight?", + "steps": [ + { + "description": "Forces", + "content": "There are three forces acting on the weight: the force of gravity, the normal force, and the force of static friction.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Normal Force", + "magnitude": 8.817, + "directionInDegrees": 64, + "component": false + }, + { + "description": "Friction Force", + "magnitude": 4.3, + "directionInDegrees": 154, + "component": false + } + ], + "showMagnitude": false + }, + { + "description": "Force of Gravity", + "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "Normal Force", + "content": "The normal force acts in the direction perpendicular to the incline plane: π/2-θ rad, where θ is the angle of the incline plane. The magnitude of the normal force is equal to m*g*cos(θ).", + "forces": [ + { + "description": "Normal Force", + "magnitude": 8.817, + "directionInDegrees": 64, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "Force of Static Friction", + "content": "The force of static friction acts in the direction along the incline plane: π-θ rad, where θ is the angle of the incline plane. We can use the knowledge that the system is in equilibrium to solve for the magnitude of the force of static friction.", + "forces": [ + { + "description": "Friction Force", + "magnitude": 4.3, + "directionInDegrees": 154, + "component": false + } + ], + "showMagnitude": false + }, + { + "description": "Net X Force in Equilibrium", + "content": "For the system to be in equilibrium, the sum of the x components of all forces must equal 0. The x component of the normal force is equal to m*g*cos(θ)*cos(π/2-θ), where θ is the angle of the incline plane. The x component of gravity is equal to 0. Since the net force in the x direction must be 0, we know the magnitude of the x component of the friction force is m*g*cos(θ)*cos(π/2-θ).", + "forces": [ + { + "description": "Normal Force - X Component", + "magnitude": 3.87, + "directionInDegrees": 0, + "component": true + }, + { + "description": "Friction Force - X Component", + "magnitude": 3.87, + "directionInDegrees": 180, + "component": true + } + ], + "showMagnitude": true + }, + { + "description": "Net Y Force Normal Force", + "content": "For the system to be in equilibrium, the sum of the y components of all forces must equal 0. The y component of the normal force is equal to m*g*cos(θ)*sin(π/2-θ), where θ is the angle of the incline plane. The y component of gravity is equal to m*g. Since the net force in the x direction must be 0, we know the magnitude of the y component of the friction force is m*g-m*g*cos(θ)*sin(π/2-θ).", + "forces": [ + { + "description": "Normal Force - Y Component ", + "magnitude": 7.92, + "directionInDegrees": 90, + "component": true + }, + { + "description": "Gravity - Y Component ", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": true + }, + { + "description": "Friction Force - Y Component ", + "magnitude": 1.89, + "directionInDegrees": 90, + "component": true + } + ], + "showMagnitude": true + }, + { + "description": "Magnitude of Force of Friction", + "content": "Combining the x and y components of the friction force, we get the magnitude of the friction force is equal to sqrt((m*g*cos(θ)*cos(π/2-θ))^2 + (m*g*cos(θ)*sin(π/2-θ)-m*g)^2).", + "forces": [ + { + "description": "Friction Force", + "magnitude": 4.3, + "directionInDegrees": 154, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "All Forces", + "content": "Combining all of the forces, we get the following free body diagram.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Normal Force", + "magnitude": 8.817, + "directionInDegrees": 64, + "component": false + }, + { + "description": "Friction Force", + "magnitude": 4.3, + "directionInDegrees": 154, + "component": false + } + ], + "showMagnitude": true + } + ] + }, + "spring": { + "question": "A 1kg weight is on a spring of negligible mass with rest length 200m and spring constant 0.5. What is the equilibrium spring length?", + "steps": [ + { + "description": "Forces", + "content": "We can start by solving for the forces acting on the weight at any given point in time. There are two forces potentially acting on the weight: the force of gravity and the spring force. In equilibrium, these forces will be perfectly balanced.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Spring Force", + "magnitude": 9.81, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": false + }, + { + "description": "Force of Gravity", + "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "Spring Force", + "content": "The spring force acts in the negative y direction (3π/2 rad) if the spring is compressed. The spring force acts in the positive y direction (π/2 rad) if the spring is extended. Because the forces are perfectly balanced and gravity acts in the negative y direction, the spring force must act in the positive y direction and have the same magnitude as the force og gravity, m*g. We can approximate g as 9.81.", + "forces": [ + { + "description": "Spring Force", + "magnitude": 9.81, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "Spring Force", + "content": "We can use the spring force equation, Fs=kd to solve for the displacement such that Fs=mg. Setting them equal, we get mg=kd. Plugging in for the known values of m,g, and k, we get 1*9.81=0.5d. Solving for d, we get d=19.62 as the equilibrium starting displacement", + "forces": [ + { + "description": "Spring Force", + "magnitude": 9.81, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": true + } + ] + }, + "circular": { + "question": "A 1kg weight is attached to a 100m rod of negligible mass. The weight is undergoing uniform circular motion with tangential velocity 40 m/s. What are the magnitude and directions of the forces acting on the weight? (Ignore air resistance)", + "steps": [ + { + "description": "Forces", + "content": "There is one force acting on the weight: the centripetal force.", + "forces": [ + { + "description": "Centripetal Force", + "magnitude": 16, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": false + }, + { + "description": "Centripetal Force", + "content": "The centripetal force is always directed toward the center of the circle. The formula for solving for the magnitude of centripetal force for an object undergoing uniform circular motion is Fc=mv^2 / r. Plugging in for known values, we get Fc=1*(40^2)/100. Solving for this, we get Fc=16", + "forces": [ + { + "description": "Centripetal Force", + "magnitude": 16, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": true + } + ] + }, + "pulley": { + "question": "A 1kg red weight is attached to a simple pulley with a rope of negligible mass. A 1.5kg blue weight is attached to the other end of the simple pulley. What are the forces acting on the red weight?", + "steps": [ + { + "description": "Forces", + "content": "There are two force acting on the red weight: the force of gravity and the force of tension.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Tension", + "magnitude": 11.77, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": false + }, + { + "description": "Gravity", + "content": "The force of gravity acts in the negative y direction: 3π/2 rad. It has magnitude equal to m*g. We can approximate g as 9.81.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "Tension", + "content": "The force of tension acts in the positive y direction: π/2 rad. We know that the acceleration in a simple pulley system is (mass 2 - mass 1) * acceleration due to gravity / (mass 1 + mass 2) = (1.5-1) * 9.81 / (1.5+1) = 1.962 m/s^2. Because the acceleration is caused by the force of gravity and force of tension, we can solve for the force of tension acting on the weight as mass 1 * (a + acceleration due to gravity) = 1 * (1.962+9.81) = 11.77.", + "forces": [ + { + "description": "Tension", + "magnitude": 11.77, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "All Forces", + "content": "Combining all of the forces, we get the following free body diagram.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Tension", + "magnitude": 11.77, + "directionInDegrees": 90, + "component": false + } + ], + "showMagnitude": true + } + ] + }, + "suspension": { + "question": "A 1kg weight is attached to two rods hanging from 45° angles from the ceiling. The system is in equilibrium, i.e. the weight does not move. What are the magnitudes and directions of the forces acting on the weight?", + "steps": [ + { + "description": "Forces", + "content": "There are three force acting on the red weight: the force of gravity, the force of tension from the left rod, and the force of tension from the right rod.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Left Tension", + "magnitude": 6.94, + "directionInDegrees": 135, + "component": false + }, + { + "description": "Right Tension", + "magnitude": 6.94, + "directionInDegrees": 45, + "component": false + } + ], + "showMagnitude": false + }, + { + "description": "Force X Components", + "content": "There are two forces with x components to consider: the tension from the left rod and the tension from the right rod. These must cancel each other out so that the net x force is 0.", + "forces": [ + { + "description": "Left Tension X Component", + "magnitude": 4.907, + "directionInDegrees": 180, + "component": true + }, + { + "description": "Right Tension X Component", + "magnitude": 4.907, + "directionInDegrees": 0, + "component": true + } + ], + "showMagnitude": false + }, { + "description": "Force Y Components", + "content": "There are three forces with y components to consider: the tension from the left rod, the tension from the right rod, and the force of gravity.", + "forces": [ + { + "description": "Left Tension Y Component", + "magnitude": 4.907, + "directionInDegrees": 90, + "component": true + }, + { + "description": "Right Tension Y Component", + "magnitude": 4.907, + "directionInDegrees": 90, + "component": true + }, + { + "description": "Gravity Y Component", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": true + } + ], + "showMagnitude": false + }, { + "description": "Force Y Components", + "content": "The y components of forces must cancel each other out so that the net y force is 0. Thus, gravity = left tension y component + right tension y component. Because the x components of tension are the same and the angles of each rod are the same, the y components must be the same. Thus, the y component for each force of tension must be 9.81/2.", + "forces": [ + { + "description": "Left Tension Y Component", + "magnitude": 4.907, + "directionInDegrees": 90, + "component": true + }, + { + "description": "Right Tension Y Component", + "magnitude": 4.907, + "directionInDegrees": 90, + "component": true + }, + { + "description": "Gravity Y Component", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": true + } + ], + "showMagnitude": true + }, { + "description": "Tension", + "content": "Now that we know the y component of tension for each rod is 4.907, we can solve for the full force of tension as 4.907 = T * sin(45°) -> T = 6.94.", + "forces": [ + { + "description": "Left Tension", + "magnitude": 6.94, + "directionInDegrees": 135, + "component": false + }, + { + "description": "Right Tension", + "magnitude": 6.94, + "directionInDegrees": 45, + "component": false + } + ], + "showMagnitude": true + }, + { + "description": "All Forces", + "content": "Combining all of the forces, we get the following free body diagram.", + "forces": [ + { + "description": "Gravity", + "magnitude": 9.81, + "directionInDegrees": 270, + "component": false + }, + { + "description": "Left Tension", + "magnitude": 6.94, + "directionInDegrees": 135, + "component": false + }, + { + "description": "Right Tension", + "magnitude": 6.94, + "directionInDegrees": 45, + "component": false + } + ], + "showMagnitude": true + } + ] + } +} diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx new file mode 100644 index 000000000..8cc1d0fbf --- /dev/null +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx @@ -0,0 +1,34 @@ +import React = require('react'); + +export interface Force { + magnitude: number; + directionInDegrees: number; +} +export interface IWallProps { + length: number; + xPos: number; + yPos: number; + angleInDegrees: number; +} + +export default class Wall extends React.Component { + + constructor(props: any) { + super(props) + } + + wallStyle = { + width: this.props.angleInDegrees == 0 ? this.props.length + "%" : "5px", + height: this.props.angleInDegrees == 0 ? "5px" : this.props.length + "%", + position: "absolute" as "absolute", + left: this.props.xPos + "%", + top: this.props.yPos + "%", + backgroundColor: "#6c7b8b", + margin: 0, + padding: 0, + }; + + render () { + return (
); + } +}; diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx new file mode 100644 index 000000000..2165c8ba9 --- /dev/null +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx @@ -0,0 +1,990 @@ +import { computed, IReactionDisposer, reaction } from 'mobx'; +import { observer } from 'mobx-react'; +import './PhysicsSimulationBox.scss'; +import React = require('react'); + +interface IWallProps { + length: number; + xPos: number; + yPos: number; + angleInDegrees: number; +} +interface IForce { + description: string; + magnitude: number; + directionInDegrees: number; +} +export interface IWeightProps { + pause: () => void; + panelWidth: () => number; + panelHeight: () => number; + resetRequest: () => number; + circularMotionRadius: number; + coefficientOfKineticFriction: number; + color: string; + componentForces: () => IForce[]; + setComponentForces: (x: IForce[]) => {}; + displayXVelocity: number; + displayYVelocity: number; + elasticCollisions: boolean; + gravity: number; + mass: number; + simulationMode: string; + noMovement: boolean; + paused: boolean; + pendulumAngle: number; + pendulumLength: number; + radius: number; + showAcceleration: boolean; + showComponentForces: boolean; + showForceMagnitudes: boolean; + showForces: boolean; + showVelocity: boolean; + simulationSpeed: number; + simulationType: string; + springConstant: number; + springRestLength: number; + springStartLength: number; + startForces: () => IForce[]; + startPendulumAngle: number; + startPendulumLength: number; + startPosX: number; + startPosY: number; + startVelX: number; + startVelY: number; + timestepSize: number; + updateMassPosX: number; + updateMassPosY: number; + forcesUpdated: () => IForce[]; + setForcesUpdated: (x: IForce[]) => {}; + setPosition: (x: number | undefined, y: number | undefined) => void; + setVelocity: (x: number | undefined, y: number | undefined) => void; + setAcceleration: (x: number, y: number) => void; + setPendulumAngle: (ang: number | undefined, length: number | undefined) => void; + setSpringLength: (length: number) => void; + wallPositions: IWallProps[]; + wedgeHeight: number; + wedgeWidth: number; + xMax: number; + xMin: number; + yMax: number; + yMin: number; +} + +interface IState { + angleLabel: number; + clickPositionX: number; + clickPositionY: number; + coordinates: string; + dragging: boolean; + kineticFriction: boolean; + maxPosYConservation: number; + timer: number; + updatedStartPosX: any; + updatedStartPosY: any; + xPosition: number; + xVelocity: number; + yPosition: number; + yVelocity: number; + xAccel: number; + yAccel: number; +} +@observer +export default class Weight extends React.Component { + constructor(props: any) { + super(props); + this.state = { + angleLabel: 0, + clickPositionX: 0, + clickPositionY: 0, + coordinates: '', + dragging: false, + kineticFriction: false, + maxPosYConservation: 0, + timer: 0, + updatedStartPosX: this.props.startPosX, + updatedStartPosY: this.props.startPosY, + xPosition: this.props.startPosX, + xVelocity: this.props.startVelX, + yPosition: this.props.startPosY, + yVelocity: this.props.startVelY, + xAccel: 0, + yAccel: 0, + }; + } + + _timer: NodeJS.Timeout | undefined; + _resetDisposer: IReactionDisposer | undefined; + + componentWillUnmount() { + this._timer && clearTimeout(this._timer); + this._resetDisposer?.(); + } + componentWillUpdate(nextProps: Readonly, nextState: Readonly, nextContext: any): void { + if (nextProps.paused) { + this._timer && clearTimeout(this._timer); + this._timer = undefined; + } else if (this.props.paused) { + this._timer && clearTimeout(this._timer); + this._timer = setInterval(() => this.setState({ timer: this.state.timer + 1 }), 50); + } + } + + // Constants + @computed get draggable() { + return !['Inclined Plane', 'Pendulum'].includes(this.props.simulationType) && this.props.simulationMode === 'Freeform'; + } + @computed get panelHeight() { + return Math.max(800, this.props.panelHeight()) + 'px'; + } + @computed get panelWidth() { + return Math.max(1000, this.props.panelWidth()) + 'px'; + } + + @computed get walls() { + return ['One Weight', 'Inclined Plane'].includes(this.props.simulationType) ? this.props.wallPositions : []; + } + epsilon = 0.0001; + labelBackgroundColor = `rgba(255,255,255,0.5)`; + + // Variables + weightStyle = { + alignItems: 'center', + backgroundColor: this.props.color, + borderColor: 'black', + borderRadius: 50 + '%', + borderStyle: 'solid', + display: 'flex', + height: 2 * this.props.radius + 'px', + justifyContent: 'center', + left: this.props.startPosX + 'px', + position: 'absolute' as 'absolute', + top: this.props.startPosY + 'px', + touchAction: 'none', + width: 2 * this.props.radius + 'px', + zIndex: 5, + }; + + // Helper function to go between display and real values + getDisplayYPos = (yPos: number) => this.props.yMax - yPos - 2 * this.props.radius + 5; + gravityForce = (): IForce => ({ + description: 'Gravity', + magnitude: this.props.mass * this.props.gravity, + directionInDegrees: 270, + }); + // Update display values when simulation updates + setDisplayValues = (xPos: number = this.state.xPosition, yPos: number = this.state.yPosition, xVel: number = this.state.xVelocity, yVel: number = this.state.yVelocity) => { + this.props.setPosition(xPos, this.getDisplayYPos(yPos)); + this.props.setVelocity(xVel, yVel); + const xAccel = Math.round(this.getNewAccelerationX(this.props.forcesUpdated()) * 100) / 100; + const yAccel = (-1 * Math.round(this.getNewAccelerationY(this.props.forcesUpdated()) * 100)) / 100; + this.props.setAcceleration(xAccel, yAccel); + this.setState({ xAccel, yAccel }); + }; + componentDidMount() { + this._resetDisposer = reaction(() => this.props.resetRequest(), this.resetEverything); + } + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { + if (prevProps.simulationType != this.props.simulationType) { + this.setState({ xVelocity: this.props.startVelX, yVelocity: this.props.startVelY }); + this.setDisplayValues(); + } + + // Change pendulum angle from input field + if (prevProps.startPendulumAngle != this.props.startPendulumAngle || prevProps.startPendulumLength !== this.props.startPendulumLength) { + const length = this.props.startPendulumLength; + const x = length * Math.cos(((90 - this.props.startPendulumAngle) * Math.PI) / 180); + const y = length * Math.sin(((90 - this.props.startPendulumAngle) * Math.PI) / 180); + const xPosition = this.props.xMax / 2 - x - this.props.radius; + const yPosition = y - this.props.radius - 5; + this.setState({ xPosition, yPosition, updatedStartPosX: xPosition, updatedStartPosY: yPosition }); + this.props.setPendulumAngle(this.props.startPendulumAngle, this.props.startPendulumLength); + } + + // When display values updated by user, update real value + if (prevProps.updateMassPosX !== this.props.updateMassPosX) { + const x = Math.min(Math.max(0, this.props.updateMassPosX), this.props.xMax - 2 * this.props.radius); + this.setState({ updatedStartPosX: x, xPosition: x }); + this.props.setPosition(x, undefined); + } + if (prevProps.updateMassPosY != this.props.updateMassPosY) { + const y = Math.min(Math.max(0, this.props.updateMassPosY), this.props.yMax - 2 * this.props.radius); + const coordinatePosition = this.getDisplayYPos(y); + this.setState({ yPosition: coordinatePosition, updatedStartPosY: coordinatePosition }); + this.props.setPosition(undefined, this.getDisplayYPos(y)); + + if (this.props.displayXVelocity != this.state.xVelocity) { + this.setState({ xVelocity: this.props.displayXVelocity }); + this.props.setVelocity(this.props.displayXVelocity, undefined); + } + + if (this.props.displayYVelocity != -this.state.yVelocity) { + this.setState({ yVelocity: -this.props.displayYVelocity }); + this.props.setVelocity(undefined, this.props.displayYVelocity); + } + } + + // Make sure weight doesn't go above max height + if ((prevState.updatedStartPosY != this.state.updatedStartPosY || prevProps.startVelY != this.props.startVelY) && !isNaN(this.state.updatedStartPosY) && !isNaN(this.props.startVelY)) { + if (this.props.simulationType == 'One Weight') { + let maxYPos = this.state.updatedStartPosY; + if (this.props.startVelY != 0) { + maxYPos -= (this.props.startVelY * this.props.startVelY) / (2 * this.props.gravity); + } + if (maxYPos < 0) maxYPos = 0; + + this.setState({ maxPosYConservation: maxYPos }); + } + } + + // Check for collisions and update + if (!this.props.paused && !this.props.noMovement && prevState.timer != this.state.timer) { + let collisions = false; + if (this.props.simulationType == 'One Weight' || this.props.simulationType == 'Inclined Plane') { + const collisionsWithGround = this.checkForCollisionsWithGround(); + const collisionsWithWalls = this.checkForCollisionsWithWall(); + collisions = collisionsWithGround || collisionsWithWalls; + } + if (this.props.simulationType == 'Pulley') { + if (this.state.yPosition <= this.props.yMin + 100 || this.state.yPosition >= this.props.yMax - 100) { + collisions = true; + } + } + if (!collisions) this.update(); + + this.setDisplayValues(); + } + + // Convert from static to kinetic friction if/when weight slips on inclined plane + if (prevState.xVelocity != this.state.xVelocity) { + if (this.props.simulationType == 'Inclined Plane' && Math.abs(this.state.xVelocity) > 0.1 && this.props.simulationMode != 'Review' && !this.state.kineticFriction) { + this.setState({ kineticFriction: true }); + const normalForce: IForce = { + description: 'Normal Force', + magnitude: this.props.mass * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, + }; + const frictionForce: IForce = { + description: 'Kinetic Friction Force', + magnitude: this.props.mass * this.props.coefficientOfKineticFriction * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + directionInDegrees: 180 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, + }; + // reduce magnitude of friction force if necessary such that block cannot slide up plane + // prettier-ignore + const yForce = - this.props.gravity + + normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + + frictionForce.magnitude * Math.sin((frictionForce.directionInDegrees * Math.PI) / 180); + if (yForce > 0) { + frictionForce.magnitude = (-normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + this.props.gravity) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180); + } + + const normalForceComponent: IForce = { + description: 'Normal Force', + magnitude: this.props.mass * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, + }; + const gravityParallel: IForce = { + description: 'Gravity Parallel Component', + magnitude: this.props.mass * this.props.gravity * Math.sin(Math.PI / 2 - Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI + 180, + }; + const gravityPerpendicular: IForce = { + description: 'Gravity Perpendicular Component', + magnitude: this.props.mass * this.props.gravity * Math.cos(Math.PI / 2 - Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + directionInDegrees: 360 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, + }; + const kineticFriction = this.props.coefficientOfKineticFriction != 0 ? [frictionForce] : []; + this.props.setForcesUpdated([this.gravityForce(), normalForce, ...kineticFriction]); + this.props.setComponentForces([normalForceComponent, gravityParallel, gravityPerpendicular, ...kineticFriction]); + } + } + + // Update x position when start pos x changes + if (prevProps.startPosX != this.props.startPosX) { + if (this.props.paused && !isNaN(this.props.startPosX)) { + this.setState({ xPosition: this.props.startPosX, updatedStartPosX: this.props.startPosX }); + this.props.setPosition(this.props.startPosX, undefined); + } + } + + // Update y position when start pos y changes TODO debug + if (prevProps.startPosY != this.props.startPosY) { + if (this.props.paused && !isNaN(this.props.startPosY)) { + this.setState({ yPosition: this.props.startPosY, updatedStartPosY: this.props.startPosY ?? 0 }); + this.props.setPosition(undefined, this.getDisplayYPos(this.props.startPosY)); + } + } + + // Update wedge coordinates + if (!this.state.coordinates || this.props.yMax !== prevProps.yMax || prevProps.wedgeWidth != this.props.wedgeWidth || prevProps.wedgeHeight != this.props.wedgeHeight) { + const left = this.props.xMax * 0.25; + const coordinatePair1 = Math.round(left) + ',' + this.props.yMax + ' '; + const coordinatePair2 = Math.round(left + this.props.wedgeWidth) + ',' + this.props.yMax + ' '; + const coordinatePair3 = Math.round(left) + ',' + (this.props.yMax - this.props.wedgeHeight); + this.setState({ coordinates: coordinatePair1 + coordinatePair2 + coordinatePair3 }); + } + + if (this.state.xPosition != prevState.xPosition || this.state.yPosition != prevState.yPosition) { + this.weightStyle = { + alignItems: 'center', + backgroundColor: this.props.color, + borderColor: 'black', + borderRadius: 50 + '%', + borderStyle: 'solid', + display: 'flex', + height: 2 * this.props.radius + 'px', + justifyContent: 'center', + left: this.state.xPosition + 'px', + position: 'absolute' as 'absolute', + top: this.state.yPosition + 'px', + touchAction: 'none', + width: 2 * this.props.radius + 'px', + zIndex: 5, + }; + } + } + + // Reset simulation on reset button click + resetEverything = () => { + this.setState({ + kineticFriction: false, + xPosition: this.state.updatedStartPosX, + yPosition: this.state.updatedStartPosY, + xVelocity: this.props.startVelX, + yVelocity: this.props.startVelY, + angleLabel: Math.round(this.props.pendulumAngle * 100) / 100, + }); + this.props.setPendulumAngle(this.props.startPendulumAngle, undefined); + this.props.setForcesUpdated(this.props.startForces()); + this.props.setPosition(this.state.updatedStartPosX, this.state.updatedStartPosY); + this.props.setVelocity(this.props.startVelX, this.props.startVelY); + this.props.setAcceleration(0, 0); + setTimeout(() => this.setState({ timer: this.state.timer + 1 })); + }; + + // Compute x acceleration from forces, F=ma + getNewAccelerationX = (forceList: IForce[]) => { + // prettier-ignore + return forceList.reduce((newXacc, force) => + newXacc + (force.magnitude * Math.cos((force.directionInDegrees * Math.PI) / 180)) / this.props.mass, 0); + }; + + // Compute y acceleration from forces, F=ma + getNewAccelerationY = (forceList: IForce[]) => { + // prettier-ignore + return forceList.reduce((newYacc, force) => + newYacc + (-1 * (force.magnitude * Math.sin((force.directionInDegrees * Math.PI) / 180))) / this.props.mass, 0); + }; + + // Compute uniform circular motion forces given x, y positions + getNewCircularMotionForces = (xPos: number, yPos: number): IForce[] => { + const deltaX = (this.props.xMin + this.props.xMax) / 2 - (xPos + this.props.radius); + const deltaY = yPos + this.props.radius - (this.props.yMin + this.props.yMax) / 2; + return [ + { + description: 'Centripetal Force', + magnitude: (this.props.startVelX ** 2 * this.props.mass) / this.props.circularMotionRadius, + directionInDegrees: (Math.atan2(deltaY, deltaX) * 180) / Math.PI, + }, + ]; + }; + + // Compute spring forces given y position + getNewSpringForces = (yPos: number): IForce[] => { + const yPosPlus = yPos - this.props.springRestLength > 0; + const yPosMinus = yPos - this.props.springRestLength < 0; + return [ + this.gravityForce(), + { + description: 'Spring Force', + magnitude: this.props.springConstant * (yPos - this.props.springRestLength) * (yPosPlus ? 1 : yPosMinus ? -1 : 0), + directionInDegrees: yPosPlus ? 90 : 270, + }, + ]; + }; + + // Compute pendulum forces given position, velocity + getNewPendulumForces = (xPos: number, yPos: number, xVel: number, yVel: number): IForce[] => { + const x = this.props.xMax / 2 - xPos - this.props.radius; + const y = yPos + this.props.radius + 5; + const angle = (ang => (ang < 0 ? ang + 180 : ang))((Math.atan(y / x) * 180) / Math.PI); + + let oppositeAngle = 90 - angle; + if (oppositeAngle < 0) { + oppositeAngle = 90 - (180 - angle); + } + + const pendulumLength = Math.sqrt(x * x + y * y); + this.props.setPendulumAngle(oppositeAngle, undefined); + + const mag = this.props.mass * this.props.gravity * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength; + + return [ + this.gravityForce(), + { + description: 'Tension', + magnitude: mag, + directionInDegrees: angle, + }, + ]; + }; + + // Check for collisions in x direction + checkForCollisionsWithWall = () => { + let collision = false; + if (this.state.xVelocity !== 0) { + this.walls + .filter(wall => wall.angleInDegrees === 90) + .forEach(wall => { + const wallX = (wall.xPos / 100) * this.props.panelWidth(); + const minX = this.state.xPosition < wallX && wall.xPos < 0.35; + const maxX = this.state.xPosition + 2 * this.props.radius >= wallX && wall.xPos > 0.35; + if (minX || maxX) { + this.setState({ + xPosition: minX ? wallX + 0.01 : wallX - 2 * this.props.radius - 0.01, + xVelocity: this.props.elasticCollisions ? -this.state.xVelocity : 0, + }); + collision = true; + } + }); + } + return collision; + }; + + // Check for collisions in y direction + checkForCollisionsWithGround = () => { + let collision = false; + const minY = this.state.yPosition; + const maxY = this.state.yPosition + 2 * this.props.radius; + if (this.state.yVelocity > 0) { + this.walls.forEach(wall => { + if (wall.angleInDegrees == 0 && wall.yPos > 0.4) { + const groundY = (wall.yPos / 100) * this.props.panelHeight(); + const gravity = this.gravityForce(); + if (maxY > groundY) { + this.setState({ yPosition: groundY - 2 * this.props.radius - 0.01 }); + if (this.props.elasticCollisions) { + this.setState({ yVelocity: -this.state.yVelocity }); + } else { + this.setState({ yVelocity: 0 }); + const normalForce: IForce = { + description: 'Normal force', + magnitude: gravity.magnitude, + directionInDegrees: -gravity.directionInDegrees, + }; + this.props.setForcesUpdated([gravity, normalForce]); + if (this.props.simulationType === 'Inclined Plane') { + this.props.setComponentForces([gravity, normalForce]); + } + } + collision = true; + } + } + }); + } + if (this.state.yVelocity < 0) { + this.walls.forEach(wall => { + if (wall.angleInDegrees == 0 && wall.yPos < 0.4) { + const groundY = (wall.yPos / 100) * this.props.panelHeight(); + if (minY < groundY) { + this.setState({ + yPosition: groundY + 0.01, + yVelocity: this.props.elasticCollisions ? -this.state.yVelocity : 0, + }); + collision = true; + } + } + }); + } + return collision; + }; + + // Called at each RK4 step + evaluate = (currentXPos: number, currentYPos: number, currentXVel: number, currentYVel: number, currdeltaXPos: number, currdeltaYPos: number, currdeltaXVel: number, currdeltaYVel: number, dt: number) => { + const xPos = currentXPos + currdeltaXPos * dt; + const yPos = currentYPos + currdeltaYPos * dt; + const xVel = currentXVel + currdeltaXVel * dt; + const yVel = currentYVel + currdeltaYVel * dt; + const forces = this.getForces(xPos, yPos, xVel, yVel); + return { + xPos, + yPos, + xVel, + yVel, + deltaXPos: xVel, + deltaYPos: yVel, + deltaXVel: this.getNewAccelerationX(forces), + deltaYVel: this.getNewAccelerationY(forces), + }; + }; + + getForces = (xPos: number, yPos: number, xVel: number, yVel: number) => { + // prettier-ignore + switch (this.props.simulationType) { + case 'Pendulum': return this.getNewPendulumForces(xPos, yPos, xVel, yVel); + case 'Spring' : return this.getNewSpringForces(yPos); + case 'Circular Motion': return this.getNewCircularMotionForces(xPos, yPos); + default: return this.props.forcesUpdated(); + } + }; + + // Update position, velocity using RK4 method + update = () => { + const startXVel = this.state.xVelocity; + const startYVel = this.state.yVelocity; + let xPos = this.state.xPosition; + let yPos = this.state.yPosition; + let xVel = this.state.xVelocity; + let yVel = this.state.yVelocity; + const forces = this.getForces(xPos, yPos, xVel, yVel); + const xAcc = this.getNewAccelerationX(forces); + const yAcc = this.getNewAccelerationY(forces); + const coeff = (this.props.timestepSize * 1.0) / 6.0; + for (let i = 0; i < this.props.simulationSpeed; i++) { + const k1 = this.evaluate(xPos, yPos, xVel, yVel, xVel, yVel, xAcc, yAcc, 0); + const k2 = this.evaluate(xPos, yPos, xVel, yVel, k1.deltaXPos, k1.deltaYPos, k1.deltaXVel, k1.deltaYVel, this.props.timestepSize * 0.5); + const k3 = this.evaluate(xPos, yPos, xVel, yVel, k2.deltaXPos, k2.deltaYPos, k2.deltaXVel, k2.deltaYVel, this.props.timestepSize * 0.5); + const k4 = this.evaluate(xPos, yPos, xVel, yVel, k3.deltaXPos, k3.deltaYPos, k3.deltaXVel, k3.deltaYVel, this.props.timestepSize); + + xVel += coeff * (k1.deltaXVel + 2 * (k2.deltaXVel + k3.deltaXVel) + k4.deltaXVel); + yVel += coeff * (k1.deltaYVel + 2 * (k2.deltaYVel + k3.deltaYVel) + k4.deltaYVel); + xPos += coeff * (k1.deltaXPos + 2 * (k2.deltaXPos + k3.deltaXPos) + k4.deltaXPos); + yPos += coeff * (k1.deltaYPos + 2 * (k2.deltaYPos + k3.deltaYPos) + k4.deltaYPos); + } + // make sure harmonic motion maintained and errors don't propagate + switch (this.props.simulationType) { + case 'Spring': + const equilibriumPos = this.props.springRestLength + (this.props.mass * this.props.gravity) / this.props.springConstant; + const amplitude = Math.abs(equilibriumPos - this.props.springStartLength); + if (startYVel < 0 && yVel > 0 && yPos < this.props.springRestLength) { + yPos = equilibriumPos - amplitude; + } else if (startYVel > 0 && yVel < 0 && yPos > this.props.springRestLength) { + yPos = equilibriumPos + amplitude; + } + break; + case 'Pendulum': + const startX = this.state.updatedStartPosX; + if (startXVel <= 0 && xVel > 0) { + xPos = this.state.updatedStartPosX; + if (this.state.updatedStartPosX > this.props.xMax / 2) { + xPos = this.props.xMax / 2 + (this.props.xMax / 2 - startX) - 2 * this.props.radius; + } + yPos = this.props.startPosY; + } else if (startXVel >= 0 && xVel < 0) { + xPos = this.state.updatedStartPosX; + if (this.state.updatedStartPosX < this.props.xMax / 2) { + xPos = this.props.xMax / 2 + (this.props.xMax / 2 - startX) - 2 * this.props.radius; + } + yPos = this.props.startPosY; + } + break; + case 'One Weight': + if (yPos < this.state.maxPosYConservation) { + yPos = this.state.maxPosYConservation; + } + } + this.setState({ xVelocity: xVel, yVelocity: yVel, xPosition: xPos, yPosition: yPos }); + + const forcesn = this.getForces(xPos, yPos, xVel, yVel); + this.props.setForcesUpdated(forcesn); + + // set component forces if they change + if (this.props.simulationType == 'Pendulum') { + const x = this.props.xMax / 2 - xPos - this.props.radius; + const y = yPos + this.props.radius + 5; + let angle = (Math.atan(y / x) * 180) / Math.PI; + if (angle < 0) { + angle += 180; + } + let oppositeAngle = 90 - angle; + if (oppositeAngle < 0) { + oppositeAngle = 90 - (180 - angle); + } + + const pendulumLength = Math.sqrt(x * x + y * y); + + const tensionComponent: IForce = { + description: 'Tension', + magnitude: this.props.mass * this.props.gravity * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength, + directionInDegrees: angle, + }; + const gravityParallel: IForce = { + description: 'Gravity Parallel Component', + magnitude: this.props.gravity * Math.cos(((90 - angle) * Math.PI) / 180), + directionInDegrees: 270 - (90 - angle), + }; + const gravityPerpendicular: IForce = { + description: 'Gravity Perpendicular Component', + magnitude: this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180), + directionInDegrees: -(90 - angle), + }; + if (this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180) < 0) { + gravityPerpendicular.magnitude = Math.abs(this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180)); + gravityPerpendicular.directionInDegrees = 180 - (90 - angle); + } + this.props.setComponentForces([tensionComponent, gravityParallel, gravityPerpendicular]); + } + }; + + renderForce = (force: IForce, index: number, asComponent: boolean, color = '#0d0d0d') => { + if (force.magnitude < this.epsilon) return; + + const angle = (force.directionInDegrees * Math.PI) / 180; + const arrowStartY = this.state.yPosition + this.props.radius - this.props.radius * Math.sin(angle); + const arrowStartX = this.state.xPosition + this.props.radius + this.props.radius * Math.cos(angle); + const arrowEndY = arrowStartY - Math.abs(force.magnitude) * Math.sin(angle) - this.props.radius * Math.sin(angle); + const arrowEndX = arrowStartX + Math.abs(force.magnitude) * Math.cos(angle) + this.props.radius * Math.cos(angle); + + let labelTop = arrowEndY + (force.directionInDegrees >= 0 && force.directionInDegrees < 180 ? 40 : -40); + let labelLeft = arrowEndX + (force.directionInDegrees > 90 && force.directionInDegrees < 270 ? -120 : 30); + + labelTop = Math.max(Math.min(labelTop, this.props.yMax + 50), this.props.yMin); + labelLeft = Math.max(Math.min(labelLeft, this.props.xMax - 60), this.props.xMin); + + return ( +
+
+ + + + + + + + +
+
+

{force.description || 'Force'}

+ {this.props.showForceMagnitudes &&

{Math.round(100 * force.magnitude) / 100} N

} +
+
+ ); + }; + + renderVector = (id: string, magX: number, magY: number, color: string, label: string) => { + const mag = Math.sqrt(magX * magX + magY * magY); + return ( +
+ + + + + + + + +
+

{label}

+
+
+ ); + }; + + // Render weight, spring, rod(s), vectors + render() { + return ( +
+
{ + if (this.draggable) { + this.props.pause(); + this.setState({ + dragging: true, + clickPositionX: e.clientX, + clickPositionY: e.clientY, + }); + } + }} + onPointerMove={e => { + if (this.state.dragging) { + let newY = this.state.yPosition + e.clientY - this.state.clickPositionY; + if (newY > this.props.yMax - 2 * this.props.radius - 10) { + newY = this.props.yMax - 2 * this.props.radius - 10; + } else if (newY < 10) { + newY = 10; + } + + let newX = this.state.xPosition + e.clientX - this.state.clickPositionX; + if (newX > this.props.xMax - 2 * this.props.radius - 10) { + newX = this.props.xMax - 2 * this.props.radius - 10; + } else if (newX < 10) { + newX = 10; + } + if (this.props.simulationType == 'Suspension') { + if (newX < (this.props.xMax + this.props.xMin) / 4 - this.props.radius - 15) { + newX = (this.props.xMax + this.props.xMin) / 4 - this.props.radius - 15; + } else if (newX > (3 * (this.props.xMax + this.props.xMin)) / 4 - this.props.radius / 2 - 15) { + newX = (3 * (this.props.xMax + this.props.xMin)) / 4 - this.props.radius / 2 - 15; + } + } + + this.setState({ yPosition: newY }); + this.props.setPosition(undefined, Math.round((this.props.yMax - 2 * this.props.radius - newY + 5) * 100) / 100); + if (this.props.simulationType != 'Pulley') { + this.setState({ xPosition: newX }); + this.props.setPosition(newX, undefined); + } + if (this.props.simulationType != 'Suspension') { + if (this.props.simulationType != 'Pulley') { + this.setState({ updatedStartPosX: newX }); + } + this.setState({ updatedStartPosY: newY }); + } + this.setState({ + clickPositionX: e.clientX, + clickPositionY: e.clientY, + }); + this.setDisplayValues(); + } + }} + onPointerUp={e => { + if (this.state.dragging) { + if (this.props.simulationType != 'Pendulum' && this.props.simulationType != 'Suspension') { + this.resetEverything(); + } + this.setState({ dragging: false }); + let newY = this.state.yPosition + e.clientY - this.state.clickPositionY; + if (newY > this.props.yMax - 2 * this.props.radius - 10) { + newY = this.props.yMax - 2 * this.props.radius - 10; + } else if (newY < 10) { + newY = 10; + } + + let newX = this.state.xPosition + e.clientX - this.state.clickPositionX; + if (newX > this.props.xMax - 2 * this.props.radius - 10) { + newX = this.props.xMax - 2 * this.props.radius - 10; + } else if (newX < 10) { + newX = 10; + } + if (this.props.simulationType == 'Spring') { + this.props.setSpringLength(newY); + } + if (this.props.simulationType == 'Suspension') { + const x1rod = (this.props.xMax + this.props.xMin) / 2 - this.props.radius - this.props.yMin - 200; + const x2rod = (this.props.xMax + this.props.xMin) / 2 + this.props.yMin + 200 + this.props.radius; + const deltaX1 = this.state.xPosition + this.props.radius - x1rod; + const deltaX2 = x2rod - (this.state.xPosition + this.props.radius); + const deltaY = this.state.yPosition + this.props.radius; + const dir1T = Math.PI - Math.atan(deltaY / deltaX1); + const dir2T = Math.atan(deltaY / deltaX2); + const tensionMag2 = (this.props.mass * this.props.gravity) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); + const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); + const tensionForce1: IForce = { + description: 'Tension', + magnitude: tensionMag1, + directionInDegrees: (dir1T * 180) / Math.PI, + }; + const tensionForce2: IForce = { + description: 'Tension', + magnitude: tensionMag2, + directionInDegrees: (dir2T * 180) / Math.PI, + }; + this.props.setForcesUpdated([tensionForce1, tensionForce2, this.gravityForce()]); + } + } + }}> +
+

{this.props.mass} kg

+
+
+ {this.props.simulationType == 'Spring' && ( +
+ + {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(val => { + const count = 10; + const xPos1 = this.state.xPosition + this.props.radius + (val % 2 === 0 ? -20 : 20); + const xPos2 = this.state.xPosition + this.props.radius + (val === 10 ? 0 : val % 2 === 0 ? 20 : -20); + const yPos1 = (val * this.state.yPosition) / count; + const yPos2 = val === 10 ? this.state.yPosition + this.props.radius : ((val + 1) * this.state.yPosition) / count; + return ; + })} + +
+ )} + + {this.props.simulationType == 'Pulley' && ( +
+ + + +
+ )} + {this.props.simulationType == 'Pulley' && ( +
+ + + +
+ )} + {this.props.simulationType == 'Suspension' && ( +
+ + + +

+ {Math.round( + ((Math.atan((this.state.yPosition + this.props.radius) / (this.state.xPosition + this.props.radius - ((this.props.xMax + this.props.xMin) / 2 - this.props.radius - this.props.yMin - 200))) * 180) / Math.PI) * 100 + ) / 100} + ° +

+
+ + + +
+

+ {Math.round( + ((Math.atan((this.state.yPosition + this.props.radius) / ((this.props.xMax + this.props.xMin) / 2 + this.props.yMin + 200 + this.props.radius - (this.state.xPosition + this.props.radius))) * 180) / Math.PI) * 100 + ) / 100} + ° +

+
+ )} + {this.props.simulationType == 'Circular Motion' && ( +
+ + + +
+ )} + {this.props.simulationType == 'Pendulum' && ( +
+ + + + {!this.state.dragging && ( +
+

+ {Math.round(this.props.pendulumLength)} m +

+

+ {Math.round(this.props.pendulumAngle * 100) / 100}° +

+
+ )} +
+ )} + {this.props.simulationType == 'Inclined Plane' && ( +
+
+ + + +
+

+ {Math.round(((Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI) * 100) / 100}° +

+
+ )} + {!this.state.dragging && + this.props.showAcceleration && + this.renderVector( + 'accArrow', + this.getNewAccelerationX(this.props.forcesUpdated()), + this.getNewAccelerationY(this.props.forcesUpdated()), + 'green', + `${Math.round(100 * Math.sqrt(this.state.xAccel * this.state.xAccel + this.state.yAccel * this.state.yAccel)) / 100} m/s^2` + )} + {!this.state.dragging && + this.props.showVelocity && + this.renderVector( + 'velArrow', + this.state.xVelocity, + this.state.yVelocity, + 'blue', + `${Math.round(100 * Math.sqrt(this.props.displayXVelocity * this.props.displayXVelocity + this.props.displayYVelocity * this.props.displayYVelocity)) / 100} m/s` + )} + {!this.state.dragging && this.props.showComponentForces && this.props.componentForces().map((force, index) => this.renderForce(force, index, true))} + {!this.state.dragging && this.props.showForces && this.props.forcesUpdated().map((force, index) => this.renderForce(force, index, false))} +
+ ); + } +} diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts new file mode 100644 index 000000000..65decc147 --- /dev/null +++ b/src/fields/DocSymbols.ts @@ -0,0 +1,26 @@ +export const Update = Symbol('DocUpdate'); +export const Self = Symbol('DocSelf'); +export const SelfProxy = Symbol('DocSelfProxy'); +export const FieldKeys = Symbol('DocFieldKeys'); +export const FieldTuples = Symbol('DocFieldTuples'); +export const Width = Symbol('DocWidth'); +export const Height = Symbol('DocHeight'); +export const Animation = Symbol('DocAnimation'); +export const Highlight = Symbol('DocHighlight'); +export const DocData = Symbol('DocData'); +export const DocLayout = Symbol('DocLayout'); +export const DocFields = Symbol('DocFields'); +export const DocCss = Symbol('DocCss'); +export const DocAcl = Symbol('DocAcl'); +export const DirectLinks = Symbol('DocDirectLinks'); +export const AclUnset = Symbol('DocAclUnset'); +export const AclPrivate = Symbol('DocAclOwnerOnly'); +export const AclReadonly = Symbol('DocAclReadOnly'); +export const AclAugment = Symbol('DocAclAugment'); +export const AclSelfEdit = Symbol('DocAclSelfEdit'); +export const AclEdit = Symbol('DocAclEdit'); +export const AclAdmin = Symbol('DocAclAdmin'); +export const UpdatingFromServer = Symbol('DocUpdatingFromServer'); +export const Initializing = Symbol('DocInitializing'); +export const ForceServerWrite = Symbol('DocForceServerWrite'); +export const CachedUpdates = Symbol('DocCachedUpdates'); -- cgit v1.2.3-70-g09d2 From 98d40d63fce7e63bce264b07dbb433dfe2b88d0c Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 15 Aug 2023 14:40:56 -0400 Subject: Freeze --- package-lock.json | 97 +++++++++++++++++++++++++++++-- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.scss | 4 ++ src/client/views/nodes/MapBox/MapBox.tsx | 56 ++++++++++++++---- src/client/views/nodes/trails/PresBox.tsx | 1 + 5 files changed, 143 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index a62b3306c..e79d94b3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6507,9 +6507,9 @@ } }, "browndash-components": { - "version": "0.0.92", - "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.92.tgz", - "integrity": "sha512-eE/6WQNZiLnaXUKyoaMm0PDYjExUsFJ9VTAIIxROpYPosIBKWNZ743xaOfmehib5us9hEXJb0CvUFJQb8rzDVw==", + "version": "0.0.93", + "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.93.tgz", + "integrity": "sha512-JL9Hi7Nq+zHZtjLCQSuGhID0Hd7X6sARutyJqbECfsLWACsIC/JfeEQZdUeH5CqO+R7pJKeoWM82AIkWGtN2Xw==", "requires": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", @@ -7063,12 +7063,97 @@ "strip-ansi": "^7.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "strip-ansi": { "version": "7.1.0", "bundled": true, "requires": { "ansi-regex": "^6.0.1" } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } } } }, @@ -8573,7 +8658,7 @@ } }, "string-width-cjs": { - "version": "npm:string-width@4.2.3", + "version": "npm:string-width-cjs@4.2.3", "bundled": true, "requires": { "emoji-regex": "^8.0.0", @@ -8596,7 +8681,7 @@ } }, "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", + "version": "npm:strip-ansi-cjs@6.0.1", "bundled": true, "requires": { "ansi-regex": "^5.0.1" @@ -8755,7 +8840,7 @@ } }, "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", + "version": "npm:wrap-ansi-cjs@7.0.0", "bundled": true, "requires": { "ansi-styles": "^4.0.0", diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index bb9f45bdd..419800ad5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1626,4 +1626,4 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSour }); embedding && DocServer.UPDATE_SERVER_CACHE(); // if a new embedding was made, update the client's server cache so that it will not come back as a promise return links; -}); +}); \ No newline at end of file diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss index 539c506c7..fcd4fc9be 100644 --- a/src/client/views/nodes/MapBox/MapBox.scss +++ b/src/client/views/nodes/MapBox/MapBox.scss @@ -11,6 +11,10 @@ padding: 12; font-size: 17; } + .mapBox-topbar{ + display:flex; + flex-direction: row; + } .mapBox-overlayButton-sidebar { background: #121721; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 15cf22953..283d57bb6 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,7 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; -import { EditableText } from 'browndash-components'; +import { EditableText, Toggle } from 'browndash-components'; import e from 'connect-flash'; import { truncateSync } from 'fs'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; @@ -65,7 +65,7 @@ const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing 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`; +script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`; document.head.appendChild(script); /** @@ -665,8 +665,11 @@ export class MapBox extends ViewBoxAnnotatableComponent { - this.removePushpin(pinDoc,pin); + pushpinDblClicked = (pin:any, pinDoc?:Doc) => { + if(pinDoc) + this.removePushpin(pinDoc,pin); + else + this._bingMap.current.entities.remove(pin); }; /* * Pushpin onclick @@ -720,7 +723,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinDblClicked(temp)); if (temp != this.searched_pin || this.searched_pin == null) { this._bingMap.current.entities.push(temp); this.searched_pin = temp; @@ -835,7 +840,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(pin,pushPin)); - this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pin,pushPin)); + this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin)); } @@ -932,11 +937,42 @@ export class MapBox extends ViewBoxAnnotatableComponent ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="..." text="Boston, MA" /> - - + ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="Boston, MA" text="Boston, MA" /> +
+ + + {/* {this.placePinOn ? : } */} + {/* {this.placePinOn ? : } + + */} + + + + +
+ - {this.placePinOn ? : } {/* {this.pinIsSelected_TEMPORARY? : null} */} diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index c0cd3ab70..76f42778b 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -52,6 +52,7 @@ export interface pinDataTypes { dataview?: boolean; poslayoutview?: boolean; dataannos?: boolean; + map?: boolean; } export interface PinProps { audioRange?: boolean; -- cgit v1.2.3-70-g09d2 From 4aeffc02334fb873341d93f300f3e153499854fd Mon Sep 17 00:00:00 2001 From: srichman333 Date: Thu, 17 Aug 2023 14:12:22 -0400 Subject: layout doc fields organized --- src/client/documents/Documents.ts | 2 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 18 +++++++-------- .../nodes/DataVizBox/components/Histogram.tsx | 27 +++++++++++----------- .../nodes/DataVizBox/components/LineChart.tsx | 5 ++-- .../views/nodes/DataVizBox/components/PieChart.tsx | 13 ++++++----- .../views/nodes/DataVizBox/components/TableBox.tsx | 6 ++--- 6 files changed, 37 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 96a983153..54f2b7132 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -679,7 +679,7 @@ export namespace Docs { DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { dataViz_title: '', _layout_fitWidth: true, nativeDimModifiable: true }, + options: { _layout_fitWidth: true, nativeDimModifiable: true }, }, ], [ diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 9c59fcd13..399d6ada0 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -47,11 +47,11 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const changedView = this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz); const changedAxes = this.axes.join('') !== StrListCast(data.config_dataVizAxes).join('') && (this.layoutDoc._dataViz_axes = new List(StrListCast(data.config_dataVizAxes))); this.layoutDoc.dataViz_selectedRows = Field.Copy(data.dataViz_selectedRows); - this.layoutDoc.histogramBarColors = Field.Copy(data.histogramBarColors); - this.layoutDoc.defaultHistogramColor = data.defaultHistogramColor; - this.layoutDoc.pieSliceColors = Field.Copy(data.pieSliceColors); + this.layoutDoc.dataViz_histogram_barColors = Field.Copy(data.dataViz_histogram_barColors); + this.layoutDoc.dataViz_histogram_defaultColor = data.dataViz_histogram_defaultColor; + this.layoutDoc.dataViz_pie_sliceColors = Field.Copy(data.dataViz_pie_sliceColors); Object.keys(this.layoutDoc).map(key => { - if (key.startsWith('histogram_title') || key.startsWith('lineChart_title') || key.startsWith('pieChart_title')) { + if (key.startsWith('dataViz_histogram_title') || key.startsWith('dataViz_lineChart_title') || key.startsWith('dataViz_pieChart_title')) { this.layoutDoc['_' + key] = data[key]; } }); @@ -74,11 +74,11 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { anchor.config_dataViz = this.dataVizView; anchor.config_dataVizAxes = this.axes.length ? new List(this.axes) : undefined; anchor.dataViz_selectedRows = Field.Copy(this.layoutDoc.dataViz_selectedRows); - anchor.histogramBarColors = Field.Copy(this.layoutDoc.histogramBarColors); - anchor.defaultHistogramColor = this.layoutDoc.defaultHistogramColor; - anchor.pieSliceColors = Field.Copy(this.layoutDoc.pieSliceColors); + anchor.dataViz_histogram_barColors = Field.Copy(this.layoutDoc.dataViz_histogram_barColors); + anchor.dataViz_histogram_defaultColor = this.layoutDoc.dataViz_histogram_defaultColor; + anchor.dataViz_pie_sliceColors = Field.Copy(this.layoutDoc.dataViz_pie_sliceColors); Object.keys(this.layoutDoc).map(key => { - if (key.startsWith('histogram_title') || key.startsWith('lineChart_title') || key.startsWith('pieChart_title')) { + if (key.startsWith('dataViz_histogram_title') || key.startsWith('dataViz_lineChart_title') || key.startsWith('dataViz_pieChart_title')) { anchor[key] = this.layoutDoc[key]; } }); @@ -155,7 +155,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { componentDidMount() { this.props.setContentView?.(this); this.fetchData(); - if (!this.layoutDoc._dataVizView) this.layoutDoc._dataVizView = this.dataVizView; + if (!this.layoutDoc.dataViz) this.layoutDoc.dataViz = this.dataVizView; } fetchData() { diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index b3bdccbbb..1eb57a323 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -107,6 +107,7 @@ export class Histogram extends React.Component { }, { fireImmediately: true } ); + if (!this.props.layoutDoc.dataViz_histogram) this.props.layoutDoc.dataViz_histogram = ''; }; @action @@ -397,7 +398,7 @@ export class Histogram extends React.Component { ) .attr('fill', d => { var barColor; - var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::')); + var barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::')); barColors.map(each => { if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1]; else { @@ -405,7 +406,7 @@ export class Histogram extends React.Component { if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1]; } }); - return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.defaultHistogramColor); + return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.dataViz_histogram_defaultColor); }); }; @@ -413,7 +414,7 @@ export class Histogram extends React.Component { this.curBarSelected.attr('fill', color); var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1); }); @@ -421,10 +422,10 @@ export class Histogram extends React.Component { }; @action eraseSelectedColor = () => { - this.curBarSelected.attr('fill', this.props.layoutDoc.defaultHistogramColor); + this.curBarSelected.attr('fill', this.props.layoutDoc.dataViz_histogram_defaultColor); var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1); }); @@ -434,11 +435,11 @@ export class Histogram extends React.Component { this._histogramData; var curSelectedBarName = ''; var titleAccessor: any = ''; - if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_histogram_' + this.props.axes[0] + '-' + this.props.axes[1]; - else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_histogram_' + this.props.axes[0]; + if (this.props.axes.length == 2) titleAccessor = 'dataViz_histogram_title' + this.props.axes[0] + '-' + this.props.axes[1]; + else if (this.props.axes.length > 0) titleAccessor = 'dataViz_histogram_title' + this.props.axes[0]; if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle; - if (!this.props.layoutDoc.defaultHistogramColor) this.props.layoutDoc.defaultHistogramColor = '#69b3a2'; - if (!this.props.layoutDoc.histogramBarColors) this.props.layoutDoc.histogramBarColors = new List(); + if (!this.props.layoutDoc.dataViz_histogram_defaultColor) this.props.layoutDoc.dataViz_histogram_defaultColor = '#69b3a2'; + if (!this.props.layoutDoc.dataViz_histogram_barColors) this.props.layoutDoc.dataViz_histogram_barColors = new List(); var selected: string; if (this._currSelected) { curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { selected += ' }'; } else selected = 'none'; var selectedBarColor; - var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::')); + var barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::')); barColors.map(each => { if (each[0] == curSelectedBarName!) selectedBarColor = each[1]; }); @@ -476,9 +477,9 @@ export class Histogram extends React.Component { tooltip={'Change Default Bar Color'} type={Type.SEC} icon={} - selectedColor={StrCast(this.props.layoutDoc.defaultHistogramColor)} - setFinalColor={undoable(color => (this.props.layoutDoc.defaultHistogramColor = color), 'Change Default Bar Color')} - setSelectedColor={undoable(color => (this.props.layoutDoc.defaultHistogramColor = color), 'Change Default Bar Color')} + selectedColor={StrCast(this.props.layoutDoc.dataViz_histogram_defaultColor)} + setFinalColor={undoable(color => (this.props.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')} + setSelectedColor={undoable(color => (this.props.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')} size={Size.XSMALL} />
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 46cf27705..d2e67e678 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -121,6 +121,7 @@ export class LineChart extends React.Component { }, { fireImmediately: true } ); + if (!this.props.layoutDoc.dataViz_lineChart) this.props.layoutDoc.dataViz_lineChart = ''; }; // anything that doesn't need to be recalculated should just be stored as drawCharts (i.e. computed values) and drawChart is gonna iterate over these observables and generate svgs based on that @@ -354,8 +355,8 @@ export class LineChart extends React.Component { render() { this.componentDidMount(); var titleAccessor: any = ''; - if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_lineChart_' + this.props.axes[0] + '-' + this.props.axes[1]; - else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_lineChart_' + this.props.axes[0]; + if (this.props.axes.length == 2) titleAccessor = 'dataViz_lineChart_title' + this.props.axes[0] + '-' + this.props.axes[1]; + else if (this.props.axes.length > 0) titleAccessor = 'dataViz_lineChart_title' + this.props.axes[0]; if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle; const selectedPt = this._currSelected ? `{ ${this.props.axes[0]}: ${this._currSelected.x} ${this.props.axes[1]}: ${this._currSelected.y} }` : 'none'; if (this._lineChartData.length>0 || (!this.incomingLinks || this.incomingLinks.length==0)){ diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 213baa8a4..6d8302b6a 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -92,6 +92,7 @@ export class PieChart extends React.Component { }, { fireImmediately: true } ); + if (!this.props.layoutDoc.dataViz_pie) this.props.layoutDoc.dataViz_pie = ''; }; @action @@ -270,7 +271,7 @@ export class PieChart extends React.Component { var sliceColor; if (dataPoint) { var accessByName = dataPoint[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ each.split('::')); + var sliceColors = StrListCast(this.props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::')); sliceColors.map(each => { if (each[0] == StrCast(accessByName)) sliceColor = each[1]; }); @@ -324,7 +325,7 @@ export class PieChart extends React.Component { this.curSliceSelected.attr('fill', color); var sliceName = this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1); }); @@ -334,10 +335,10 @@ export class PieChart extends React.Component { render() { this.componentDidMount(); var titleAccessor: any = ''; - if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0] + '-' + this.props.axes[1]; - else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0]; + if (this.props.axes.length == 2) titleAccessor = 'dataViz_pie_title' + this.props.axes[0] + '-' + this.props.axes[1]; + else if (this.props.axes.length > 0) titleAccessor = 'dataViz_pie_title' + this.props.axes[0]; if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle; - if (!this.props.layoutDoc.pieSliceColors) this.props.layoutDoc.pieSliceColors = new List(); + if (!this.props.layoutDoc.dataViz_pie_sliceColors) this.props.layoutDoc.dataViz_pie_sliceColors = new List(); var selected: string; var curSelectedSliceName = ''; if (this._currSelected) { @@ -350,7 +351,7 @@ export class PieChart extends React.Component { selected += ' }'; } else selected = 'none'; var selectedSliceColor; - var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::')); + var sliceColors = StrListCast(this.props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::')); sliceColors.map(each => { if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1]; }); diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index edec72cc5..33688462c 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -102,9 +102,9 @@ export class TableBox extends React.Component { embedding._dataViz_axes = new List([col, col]); embedding._draggedFrom = this.props.docView?.()!.rootDoc!; embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; - embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors); - embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor; - embedding.pieSliceColors = Field.Copy(this.props.layoutDoc.pieSliceColors); + embedding.dataViz_histogram_barColors = Field.Copy(this.props.layoutDoc.dataViz_histogram_barColors); + embedding.dataViz_histogram_defaultColor = this.props.layoutDoc.dataViz_histogram_defaultColor; + embedding.dataViz_pie_sliceColors = Field.Copy(this.props.layoutDoc.dataViz_pie_sliceColors); return embedding; }; if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { -- cgit v1.2.3-70-g09d2 From f91039f27fda5e1c10e6a8f11920ae2a2b305a16 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Fri, 18 Aug 2023 15:18:43 -0400 Subject: LineChart cleanup --- .../nodes/DataVizBox/components/LineChart.tsx | 52 +++++----------------- 1 file changed, 10 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index d2e67e678..2b2a5326a 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -88,10 +88,6 @@ export class LineChart extends React.Component { ({ dataSet, w, h }) => { if (dataSet) { this.drawChart([dataSet], this.rangeVals, w, h); - // redraw annotations when the chart data has changed, or the local or inherited selection has changed - this.clearAnnotations(); - this._currSelected && this.drawAnnotations(Number(this._currSelected.x), Number(this._currSelected.y), true); - this.incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]]))); } }, { fireImmediately: true } @@ -103,7 +99,7 @@ export class LineChart extends React.Component { // could be blue colored to make it look like anchor // this.drawAnnotations() // loop through annotations and draw them - annotations.forEach(a => this.drawAnnotations(Number(a.x), Number(a.y))); + // annotations.forEach(a => this.drawAnnotations(Number(a.x), Number(a.y))); // this.drawAnnotations(annotations.x, annotations.y); }, { fireImmediately: true } @@ -115,49 +111,21 @@ export class LineChart extends React.Component { }), ({ selected, incomingSelected }) => { // redraw annotations when the chart data has changed, or the local or inherited selection has changed - this.clearAnnotations(); - selected && this.drawAnnotations(Number(selected.x), Number(selected.y), true); - incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]]))); + if (selected){ + const elements = document.querySelectorAll('.datapoint'); + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + const x = element.getAttribute('data-x'); + const y = element.getAttribute('data-y'); + if (Number(x) === Number(selected.x) && Number(y) === Number(selected.y)) element.classList.add('selected'); + } + } }, { fireImmediately: true } ); if (!this.props.layoutDoc.dataViz_lineChart) this.props.layoutDoc.dataViz_lineChart = ''; }; - // anything that doesn't need to be recalculated should just be stored as drawCharts (i.e. computed values) and drawChart is gonna iterate over these observables and generate svgs based on that - - clearAnnotations = () => { - const elements = document.querySelectorAll('.datapoint'); - for (let i = 0; i < elements.length; i++) { - const element = elements[i]; - element.classList.remove('brushed'); - element.classList.remove('selected'); - } - }; - // gets called whenever the "data_annotations" fields gets updated - drawAnnotations = (dataX: number, dataY: number, selected?: boolean) => { - // TODO: nda - can optimize this by having some sort of mapping of the x and y values to the individual circle elements - // loop through all html elements with class .circle-d1 and find the one that has "data-x" and "data-y" attributes that match the dataX and dataY - // if it exists, then highlight it - // if it doesn't exist, then remove the highlight - const elements = document.querySelectorAll('.datapoint'); - for (let i = 0; i < elements.length; i++) { - const element = elements[i]; - const x = element.getAttribute('data-x'); - const y = element.getAttribute('data-y'); - if (x === dataX.toString() && y === dataY.toString()) { - element.classList.add(selected ? 'selected' : 'brushed'); - } - // TODO: nda - this remove highlight code should go where we remove the links - // } else { - // } - } - }; - - removeAnnotations(dataX: number, dataY: number) { - // loop through and remove any annotations that no longer exist - } - @action restoreView = (data: Doc) => { const coords = Cast(data.config_dataVizSelection, listSpec('number'), null); -- cgit v1.2.3-70-g09d2 From eb35837e5ffe8e7eba9decea1fae5c163528dc1e Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 21 Aug 2023 11:59:05 -0400 Subject: Buggy --- src/client/documents/Documents.ts | 5 +- src/client/views/nodes/MapBox/MapBox.scss | 4 + src/client/views/nodes/MapBox/MapBox.tsx | 201 +++++++++++++----------- src/client/views/nodes/MapBox/MapPushpinBox.tsx | 4 +- src/client/views/nodes/trails/PresBox.tsx | 5 +- 5 files changed, 120 insertions(+), 99 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index af4cf2243..eb777aae2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -368,8 +368,6 @@ export class DocumentOptions { waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait onPointerDown?: ScriptField; onPointerUp?: ScriptField; - openFactoryLocation?: string; // an OpenWhere value to place the factory created document - openFactoryAsDelegate?: BOOLt = new BoolInfo('create a delegate of the factory'); _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)'); _dragOnlyWithinContainer?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere'); _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.'); @@ -381,7 +379,6 @@ export class DocumentOptions { cloneFieldFilter?: List; // fields not to copy when the document is clonedclipboard?: Doc; dragWhenActive?: BOOLt = new BoolInfo('should document drag when it is active - e.g., pileView, group'); dragAction?: DROPt = new DAInfo('how to drag document when it is active (e.g., tree, groups)'); - dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true); dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop @@ -1104,7 +1101,7 @@ export namespace Docs { ); } export function MapanchorDocument(options: DocumentOptions = {}, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id); + return InstanceFromProto(Prototypes.get(DocumentType.MAP), options?.data, options, id); } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss index fcd4fc9be..39fa3262e 100644 --- a/src/client/views/nodes/MapBox/MapBox.scss +++ b/src/client/views/nodes/MapBox/MapBox.scss @@ -11,6 +11,10 @@ padding: 12; font-size: 17; } + .mapBox-searchbar{ + display:flex; + flex-direction: row; + } .mapBox-topbar{ display:flex; flex-direction: row; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 283d57bb6..bad1a3ebd 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,7 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; -import { EditableText, Toggle } from 'browndash-components'; +import { EditableText, IconButton, Toggle, Type } from 'browndash-components'; import e from 'connect-flash'; import { truncateSync } from 'fs'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; @@ -13,12 +13,12 @@ import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { ScriptField } from '../../../../fields/ScriptField'; import { NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnIgnore, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; -import { UndoManager } from '../../../util/UndoManager'; +import { undoable, UndoManager } from '../../../util/UndoManager'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; @@ -116,7 +116,7 @@ export class MapBox extends ViewBoxAnnotatableComponent(); @observable private searchMarkers: google.maps.Marker[] = []; - // @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); + @observable private searchBox = undefined as any; // new window.google.maps.places.Autocomplete(this.inputRef.current!, options); @observable private _savedAnnotations = new ObservableMap(); @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); @@ -138,45 +138,6 @@ export class MapBox extends ViewBoxAnnotatableComponent = React.createRef(); private _disposer: {[key:string]:IReactionDisposer} = {} componentDidMount() { - this.props.setContentView?.(this); - this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}), - (locationObject) => { - - this._bingMap.current.setView({ - mapTypeId: locationObject.mapType, - zoom:locationObject.zoom, - center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng), - }); - - }, {fireImmediately: true}); - // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}), - // (pinsObject) => { - // this._bingMap.current.entities.clear(); - // pinsObject.pinList.map((pushpin: Doc) => ( - // new ScriptField(undefined)} - // onKey={undefined} - // onDoubleClick={undefined} - // onBrowseClick={undefined} - // docFilters={returnEmptyDoclist} - // docRangeFilters={returnEmptyDoclist} - // searchFilterDocs={returnEmptyDoclist} - // isDocumentActive={returnFalse} - // isContentActive={returnFalse} - // addDocTab={returnFalse} - // ScreenToLocalTransform={()=>new Transform(0,0,0)} - // fitContentsToBox={undefined} - // focus={returnOne} - // />)); - - // }, {fireImmediately: false}); } @@ -638,7 +599,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { + pushpinClicked = (pinDoc:Doc) => { // TODO: // if (sidebarannos is not open) open sidebarannos // creates button onclick removes the doc from annotations @@ -685,27 +646,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { - // if (e.button === 2 || e.ctrlKey) { - // AnchorMenu.Instance.Status = 'annotation'; - // AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this); - // AnchorMenu.Instance.Pinned = false; - // AnchorMenu.Instance.PinToPres = this.pinToPres; - // AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle; - // AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler; - // AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion); - // AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true); - // e.stopPropagation(); - // } else if (e.button === 0) { - // e.stopPropagation(); - // LinkFollower.FollowLink(undefined, this.annoTextRegion, false); - // } - // }; - - // TODO: UPDATE FOR DASHDOC SELECTION }; /** @@ -811,10 +751,10 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(pin,pushPin)); + this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin)); this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin)); } @@ -876,7 +816,7 @@ export class MapBox extends ViewBoxAnnotatableComponent{ + this.bingSearchBarContents = newText + } + /* * Called when BingMap is first rendered * Initializes starting values */ bingMapReady = (map: any) => { this._bingMap = map.map; - this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick); - this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', this.updateLayout); - this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', this.updateMapType); - this.updateLayout(); - this.updateMapType(); + if (!this._bingMap.current) { + alert("NO Map!?") + } + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, "Added Pin to Map")); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, "Map Layout Change")); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, "Map ViewType Change")); + //this.updateLayout(); + // this.updateMapType(); + this.props.setContentView?.(this); + this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}), + (locationObject) => { + // if (this._bingMap.current) + this._bingMap.current?.setView({ + mapTypeId: locationObject.mapType, + zoom:locationObject.zoom, + center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng), + }); + + }, {fireImmediately: true}); + // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}), + // (pinsObject) => { + // this._bingMap.current.entities.clear(); + // pinsObject.pinList.map((pushpin: Doc) => ( + // new ScriptField(undefined)} + // onKey={undefined} + // onDoubleClick={undefined} + // onBrowseClick={undefined} + // docFilters={returnEmptyDoclist} + // docRangeFilters={returnEmptyDoclist} + // searchFilterDocs={returnEmptyDoclist} + // isDocumentActive={returnFalse} + // isContentActive={returnFalse} + // addDocTab={returnFalse} + // ScreenToLocalTransform={()=>new Transform(0,0,0)} + // fitContentsToBox={undefined} + // focus={returnOne} + // />)); + + // }, {fireImmediately: false}); + } + searchbarKeyDown = (e:any)=>{ + if (e.key === 'Enter') { + this.bingSearch() + } + } - render() { const renderAnnotations = (childFilters?: () => string[]) => null; return ( @@ -937,9 +929,44 @@ export class MapBox extends ViewBoxAnnotatableComponent ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="Boston, MA" text="Boston, MA" /> + +
+ typeof newText === "string" && this.searchbarOnEdit(newText)} + placeholder="Boston" + // text="text" + /> +
+ +
- + {/* */} + {/* {this.placePinOn ? : } */} {/* {this.placePinOn ? : } @@ -957,19 +984,7 @@ export class MapBox extends ViewBoxAnnotatableComponent */} - - - +
@@ -992,8 +1007,8 @@ export class MapBox extends ViewBoxAnnotatableComponent() { return FieldView.LayoutString(MapPushpinBox, fieldKey); } componentDidMount() { - this.mapBoxView.addPushpin(this.rootDoc); + // if (this.mapBoxView) + this.mapBoxView.addPushpin(this.rootDoc); } componentWillUnmount() { // this.mapBoxView.removePushpin(this.rootDoc); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 76f42778b..1560ce3e1 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -659,7 +659,10 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presYRange = undefined; //targetDoc?.yrange; } if (pinProps.pinData.map) { - pinDoc.presLat = targetDoc?.lat; + pinDoc.presLat = targetDoc?.latitude; + pinDoc.presLong = targetDoc?.longitude; + pinDoc.presZoom = targetDoc?.zoom; + pinDoc.mapType = targetDoc?.mapType; //... } if (pinProps.pinData.poslayoutview) -- cgit v1.2.3-70-g09d2 From 2207673156981e6313909023dbbde3d1a7f4d7a6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 21 Aug 2023 12:21:48 -0400 Subject: merged with master --- src/client/views/nodes/MapBox/MapBox.tsx | 293 ++++++++++++++----------------- 1 file changed, 135 insertions(+), 158 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 9adae0a6e..f93126e04 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -116,7 +116,7 @@ export class MapBox extends ViewBoxAnnotatableComponent(); @observable private searchMarkers: google.maps.Marker[] = []; - @observable private searchBox = undefined as any; // new window.google.maps.places.Autocomplete(this.inputRef.current!, options); + @observable private searchBox = undefined as any; // new window.google.maps.places.Autocomplete(this.inputRef.current!, options); @observable private _savedAnnotations = new ObservableMap(); @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); @@ -136,14 +136,11 @@ export class MapBox extends ViewBoxAnnotatableComponent(); private _ref: React.RefObject = React.createRef(); - private _disposer: {[key:string]:IReactionDisposer} = {} - componentDidMount() { - - - } + private _disposer: { [key: string]: IReactionDisposer } = {}; + componentDidMount() {} componentWillUnmount(): void { - Object.keys(this._disposer).forEach(key => this._disposer[key]?.()) + Object.keys(this._disposer).forEach(key => this._disposer[key]?.()); } // iterate allMarkers to size, center, and zoom map to contain all markers private fitBounds = (map: google.maps.Map) => { @@ -606,7 +603,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { // Stores the pushpin as a MapMarkerDocument - const mapMarker = Docs.Create.PushpinDocument(NumCast(latitude), NumCast(longitude), false, [], {} - // ,'pushpinIDamongus'+ this.incrementer++ + const mapMarker = Docs.Create.PushpinDocument( + NumCast(latitude), + NumCast(longitude), + false, + [], + {} + // ,'pushpinIDamongus'+ this.incrementer++ ); this.addDocument(mapMarker, this.annotationKey); @@ -626,17 +627,15 @@ export class MapBox extends ViewBoxAnnotatableComponent { - if(pinDoc) - this.removePushpin(pinDoc,pin); - else - this._bingMap.current.entities.remove(pin); + pushpinDblClicked = (pin: any, pinDoc?: Doc) => { + if (pinDoc) this.removePushpin(pinDoc, pin); + else this._bingMap.current.entities.remove(pin); }; /* * Pushpin onclick */ @action - pushpinClicked = (pinDoc:Doc) => { + pushpinClicked = (pinDoc: Doc) => { // TODO: // if (sidebarannos is not open) open sidebarannos // creates button onclick removes the doc from annotations @@ -644,8 +643,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { - this.dataDoc.latitude = this._bingMap.current.getCenter().latitude; - this.dataDoc.longitude = this._bingMap.current.getCenter().longitude; - this.dataDoc.zoom = this._bingMap.current.getZoom(); - // if(this.dataDoc.mapType == 'x'){ - // this.dataDoc.locationToLookAt - // } - // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId(); + this.dataDoc.latitude = this._bingMap.current.getCenter().latitude; + this.dataDoc.longitude = this._bingMap.current.getCenter().longitude; + this.dataDoc.mapZoom = this._bingMap.current.getZoom(); + // if(this.dataDoc.mapType == 'x'){ + // this.dataDoc.locationToLookAt + // } + // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId(); }; /* * Updates maptype */ @action updateMapType = () => { - this.dataDoc.mapType = this._bingMap.current.getMapTypeId(); - + this.dataDoc.mapType = this._bingMap.current.getMapTypeId(); }; - searched_pin: any; /* * For Bing Maps @@ -704,7 +696,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinDblClicked(temp)); if (temp != this.searched_pin || this.searched_pin == null) { this._bingMap.current.entities.push(temp); this.searched_pin = temp; } - }; /** @@ -745,18 +733,17 @@ export class MapBox extends ViewBoxAnnotatableComponent { - const anchor = - Docs.Create.MapanchorDocument({ - title: 'MapAnchor:' + this.rootDoc.title, - presLat: NumCast(this.dataDoc.latitude), - presLong: NumCast(this.dataDoc.longitude), - presZoom: NumCast(this.dataDoc.zoom), - presMapType: StrCast(this.dataDoc.mapType), - // preslocationToLookAt:this.dataDoc.locationToLookAt, - // presType: - layout_unrendered: true, - annotationOn: this.rootDoc, - }); + const anchor = Docs.Create.MapanchorDocument({ + title: 'MapAnchor:' + this.rootDoc.title, + config_latitude: NumCast(this.dataDoc.latitude), + config_longitude: NumCast(this.dataDoc.longitude), + config_zoom: NumCast(this.dataDoc.mapZoom), + config_mapType: StrCast(this.dataDoc.mapType), + // preslocationToLookAt:this.dataDoc.locationToLookAt, + // presType: + layout_unrendered: true, + annotationOn: this.rootDoc, + }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; /* addAsAnnotation &&*/ this.addDocument(anchor); @@ -767,33 +754,33 @@ export class MapBox extends ViewBoxAnnotatableComponent { - const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng), - // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'} - ); + const pushPin = pin.infoWindowOpen + ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), {}) + : new this.MicrosoftMaps.Pushpin( + new this.MicrosoftMaps.Location(pin.latitude, pin.longitude) + // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'} + ); - this._bingMap.current.entities.push(pushPin); this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin)); this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin)); - } - - + }; @observable - pinIsSelected_TEMPORARY:boolean=false; // toggles if remove pin button appears + pinIsSelected_TEMPORARY: boolean = false; // toggles if remove pin button appears /* - * Input: pin doc - * Removes MicrosoftMaps Pushpin to the map (render) - */ + * Input: pin doc + * Removes MicrosoftMaps Pushpin to the map (render) + */ @action - removePushpin = (pinDoc:Doc,pin:any)=>{ + removePushpin = (pinDoc: Doc, pin: any) => { // this.allMapPushpins // this.allMapPushpins.map(pin => this.addPushpin(pin)); // this._bingMap.current.entities.clear(); @@ -801,7 +788,7 @@ export class MapBox extends ViewBoxAnnotatableComponent{ - this.bingSearchBarContents = newText - } + searchbarOnEdit = (newText: string) => { + this.bingSearchBarContents = newText; + }; /* - * Called when BingMap is first rendered + * Called when BingMap is first rendered * Initializes starting values */ bingMapReady = (map: any) => { this._bingMap = map.map; if (!this._bingMap.current) { - alert("NO Map!?") + alert('NO Map!?'); } - this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, "Added Pin to Map")); - this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, "Map Layout Change")); - this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, "Map ViewType Change")); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, 'Added Pin to Map')); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change')); + this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change')); //this.updateLayout(); - // this.updateMapType(); + // this.updateMapType(); this.props.setContentView?.(this); - this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}), - (locationObject) => { - // if (this._bingMap.current) + this._disposer.location = reaction( + () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }), + locationObject => { + // if (this._bingMap.current) this._bingMap.current?.setView({ mapTypeId: locationObject.mapType, - zoom:locationObject.zoom, + zoom: locationObject.zoom, center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng), }); - - }, {fireImmediately: true}); - // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}), + }, + { fireImmediately: true } + ); + // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}), // (pinsObject) => { // this._bingMap.current.entities.clear(); // pinsObject.pinList.map((pushpin: Doc) => ( @@ -881,7 +870,7 @@ export class MapBox extends ViewBoxAnnotatableComponentnew ScriptField(undefined)} // onKey={undefined} @@ -899,13 +888,12 @@ export class MapBox extends ViewBoxAnnotatableComponent)); // }, {fireImmediately: false}); - } - searchbarKeyDown = (e:any)=>{ + }; + searchbarKeyDown = (e: any) => { if (e.key === 'Enter') { - this.bingSearch() - } - - } + this.bingSearch(); + } + }; render() { const renderAnnotations = (childFilters?: () => string[]) => null; return ( @@ -929,44 +917,39 @@ export class MapBox extends ViewBoxAnnotatableComponent - typeof newText === "string" && this.searchbarOnEdit(newText)} - placeholder="Boston" - // text="text" - /> +
+ typeof newText === 'string' && this.searchbarOnEdit(newText)} + placeholder="Boston" + // text="text" + />
- - -
+ +
{/* */} - {/* {this.placePinOn ? : } */} {/* {this.placePinOn ? : } @@ -983,41 +966,35 @@ export class MapBox extends ViewBoxAnnotatableComponent */} - -
- {/* {this.pinIsSelected_TEMPORARY? : null} */} - - +
- - {this.allMapPushpins - .map(pushpin => ( - new ScriptField(undefined)} - onKey={undefined} - onDoubleClick={undefined} - onBrowseClick={undefined} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - isDocumentActive={returnFalse} - isContentActive={returnFalse} - addDocTab={returnFalse} - ScreenToLocalTransform={()=>new Transform(0,0,0)} - fitContentsToBox={undefined} - focus={returnOne} - /> - ))} + {this.allMapPushpins.map(pushpin => ( + new ScriptField(undefined)} + onKey={undefined} + onDoubleClick={undefined} + onBrowseClick={undefined} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + isDocumentActive={returnFalse} + isContentActive={returnFalse} + addDocTab={returnFalse} + ScreenToLocalTransform={() => new Transform(0, 0, 0)} + fitContentsToBox={undefined} + focus={returnOne} + /> + ))}
{/* Date: Mon, 21 Aug 2023 12:23:39 -0400 Subject: from last --- src/client/views/nodes/MapBox/MapBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index f93126e04..50d1aa9d6 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -737,7 +737,7 @@ export class MapBox extends ViewBoxAnnotatableComponent Date: Mon, 21 Aug 2023 12:40:39 -0400 Subject: fixed calling setComponentView for maps --- src/client/views/nodes/MapBox/MapBox.tsx | 147 ++---------------------- src/client/views/nodes/MapBox/MapPushpinBox.tsx | 9 +- 2 files changed, 12 insertions(+), 144 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 50d1aa9d6..860528ec4 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,9 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; -import { EditableText, IconButton, Toggle, Type } from 'browndash-components'; -import e from 'connect-flash'; -import { truncateSync } from 'fs'; +import { EditableText, IconButton, Type } from 'browndash-components'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -13,7 +11,7 @@ import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { ScriptField } from '../../../../fields/ScriptField'; import { NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnIgnore, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; @@ -23,14 +21,12 @@ import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; -import { AnchorMenu } from '../../pdf/AnchorMenu'; import { Annotation } from '../../pdf/Annotation'; import { SidebarAnnos } from '../../SidebarAnnos'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps, PresBox } from '../trails'; import './MapBox.scss'; -import { MapBoxInfoWindow } from './MapBoxInfoWindow'; // amongus /** * MapBox architecture: @@ -62,11 +58,11 @@ const mapOptions = { 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 script = document.createElement('script'); -script.defer = true; -script.async = true; -script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`; -document.head.appendChild(script); +// const script = document.createElement('script'); +// script.defer = true; +// script.async = true; +// script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`; +// document.head.appendChild(script); /** * Consider integrating later: allows for drawing, circling, making shapes on map @@ -84,13 +80,6 @@ document.head.appendChild(script); // }, // }); -// options for searchbox in Google Maps Places Autocomplete API -const options = { - fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields - strictBounds: false, - types: ['establishment'], // type pf places, subject of change according to user need -} as google.maps.places.AutocompleteOptions; - @observer export class MapBox extends ViewBoxAnnotatableComponent>() { private _dropDisposer?: DragManager.DragDropDisposer; @@ -137,7 +126,9 @@ export class MapBox extends ViewBoxAnnotatableComponent(); private _ref: React.RefObject = React.createRef(); private _disposer: { [key: string]: IReactionDisposer } = {}; - componentDidMount() {} + componentDidMount() { + this.props.setContentView?.(this); + } componentWillUnmount(): void { Object.keys(this._disposer).forEach(key => this._disposer[key]?.()); @@ -149,74 +140,6 @@ export class MapBox extends ViewBoxAnnotatableComponent bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds())); }; - /** - * Custom control for add marker button - * @param controlDiv - * @param map - */ - private CenterControl = () => { - const controlDiv = document.createElement('div'); - controlDiv.className = 'mapBox-addMarker'; - // Set CSS for the control border. - const controlUI = document.createElement('div'); - controlUI.style.backgroundColor = '#fff'; - controlUI.style.borderRadius = '3px'; - controlUI.style.cursor = 'pointer'; - controlUI.style.marginTop = '10px'; - controlUI.style.borderRadius = '4px'; - controlUI.style.marginBottom = '22px'; - controlUI.style.textAlign = 'center'; - controlUI.style.position = 'absolute'; - controlUI.style.width = '32px'; - controlUI.style.height = '32px'; - controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.'; - - const plIcon = document.createElement('img'); - plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png'; - plIcon.style.color = 'rgb(25,25,25)'; - plIcon.style.fontFamily = 'Roboto,Arial,sans-serif'; - plIcon.style.fontSize = '16px'; - plIcon.style.lineHeight = '32px'; - plIcon.style.left = '18'; - plIcon.style.top = '15'; - plIcon.style.position = 'absolute'; - plIcon.width = 14; - plIcon.height = 14; - plIcon.innerHTML = 'Add'; - controlUI.appendChild(plIcon); - - // Set CSS for the control interior. - const markerIcon = document.createElement('img'); - markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png'; - markerIcon.style.color = 'rgb(25,25,25)'; - markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif'; - markerIcon.style.fontSize = '16px'; - markerIcon.style.lineHeight = '32px'; - markerIcon.style.left = '-2'; - markerIcon.style.top = '1'; - markerIcon.width = 30; - markerIcon.height = 30; - markerIcon.style.position = 'absolute'; - markerIcon.innerHTML = 'Add'; - controlUI.appendChild(markerIcon); - - // Setup the click event listeners - controlUI.addEventListener('click', () => { - if (this.toggleAddMarker === true) { - this.toggleAddMarker = false; - console.log('add marker button status:' + this.toggleAddMarker); - controlUI.style.backgroundColor = '#fff'; - markerIcon.style.color = 'rgb(25,25,25)'; - } else { - this.toggleAddMarker = true; - console.log('add marker button status:' + this.toggleAddMarker); - controlUI.style.backgroundColor = '#4476f7'; - markerIcon.style.color = 'rgb(255,255,255)'; - } - }); - controlDiv.appendChild(controlUI); - return controlDiv; - }; /** * Load and render all map markers * @param marker @@ -256,47 +179,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { - this._map = map; - this._loadPending = true; - const centerControlDiv = this.CenterControl(); - map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv); - //drawingManager.setMap(map); - // if (navigator.geolocation) { - // navigator.geolocation.getCurrentPosition( - // (position: Position) => { - // const pos = { - // lat: position.coords.latitude, - // lng: position.coords.longitude, - // }; - // this._map.setCenter(pos); - // } - // ); - // } else { - // alert("Your geolocation is not supported by browser.") - // }; - map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5)); - map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng))); - setTimeout(() => { - if (this._loadPending && this._map.getBounds()) { - this._loadPending = false; - this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); - } - }, 250); - // listener to addmarker event - this._map.addListener('click', (e: MouseEvent) => { - if (this.toggleAddMarker === true) { - this.placeMarker((e as any).latLng, map); - } - }); - }; @action centered = () => { @@ -535,14 +417,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { - // console.log("print the selected views in selectionManager:") - // if (SelectionManager.Views().lastElement()) { - // console.log(SelectionManager.Views().lastElement()); - // } - }; - 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._layout_scrollTop)); @@ -847,7 +721,6 @@ export class MapBox extends ViewBoxAnnotatableComponent ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }), locationObject => { diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx index 6369f9e04..d28209ea1 100644 --- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx +++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx @@ -5,28 +5,23 @@ import { FieldView, FieldViewProps } from '../FieldView'; import React = require('react'); import { computed } from 'mobx'; import { MapBox } from './MapBox'; -import { undoable } from '../../../util/UndoManager'; - /** * Map Pushpin doc class */ @observer export class MapPushpinBox extends ViewBoxBaseComponent() { - - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapPushpinBox, fieldKey); } componentDidMount() { // if (this.mapBoxView) - this.mapBoxView.addPushpin(this.rootDoc); + this.mapBoxView.addPushpin(this.rootDoc); } componentWillUnmount() { // this.mapBoxView.removePushpin(this.rootDoc); } - @computed get mapBoxView() { return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as MapBox; } @@ -35,6 +30,6 @@ export class MapPushpinBox extends ViewBoxBaseComponent() { } render() { - return (
); + return
; } } -- cgit v1.2.3-70-g09d2 From fbdd45bf7467267a4629a78e4ad87bae10879573 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 21 Aug 2023 12:48:19 -0400 Subject: fixed map pushpins by only rendering them after the map is ready --- src/client/views/nodes/MapBox/MapBox.tsx | 53 +++++++++++++++++--------------- 1 file changed, 29 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 860528ec4..453a3d6fc 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -711,7 +711,10 @@ export class MapBox extends ViewBoxAnnotatableComponent { + this._mapReady = true; this._bingMap = map.map; if (!this._bingMap.current) { alert('NO Map!?'); @@ -844,30 +847,32 @@ export class MapBox extends ViewBoxAnnotatableComponent : null} */}
- {this.allMapPushpins.map(pushpin => ( - new ScriptField(undefined)} - onKey={undefined} - onDoubleClick={undefined} - onBrowseClick={undefined} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - isDocumentActive={returnFalse} - isContentActive={returnFalse} - addDocTab={returnFalse} - ScreenToLocalTransform={() => new Transform(0, 0, 0)} - fitContentsToBox={undefined} - focus={returnOne} - /> - ))} + {!this._mapReady + ? null + : this.allMapPushpins.map(pushpin => ( + new ScriptField(undefined)} + onKey={undefined} + onDoubleClick={undefined} + onBrowseClick={undefined} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + isDocumentActive={returnFalse} + isContentActive={returnFalse} + addDocTab={returnFalse} + ScreenToLocalTransform={() => new Transform(0, 0, 0)} + fitContentsToBox={undefined} + focus={returnOne} + /> + ))}
{/* Date: Mon, 21 Aug 2023 13:22:22 -0400 Subject: Working on Geireann's component stuff. Now gonna try drag drop pins --- src/client/views/nodes/MapBox/MapBox.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 453a3d6fc..242611135 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,10 +1,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; -import { EditableText, IconButton, Type } from 'browndash-components'; +import { EditableText, IconButton, Toggle, Type } from 'browndash-components'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { getMenuPlacement } from 'react-select/src/components/Menu'; import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; import { Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; @@ -695,7 +696,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this.placePinOn) this.placePinOn = false; @@ -822,6 +823,13 @@ export class MapBox extends ViewBoxAnnotatableComponent

{this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}

+
-- cgit v1.2.3-70-g09d2 From f573d7f0e93fe4b2b1f64b8591c77a225170847f Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 21 Aug 2023 14:28:03 -0400 Subject: map --- src/client/views/nodes/MapBox/MapBox.tsx | 101 +++++++++++++++---------------- 1 file changed, 49 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 242611135..bd631f942 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -2,6 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; import { EditableText, IconButton, Toggle, Type } from 'browndash-components'; +import { tickStep } from 'd3'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -737,35 +738,38 @@ export class MapBox extends ViewBoxAnnotatableComponent ({pinList:this.dataDoc[this.annotationKey]}), - // (pinsObject) => { - // this._bingMap.current.entities.clear(); - // pinsObject.pinList.map((pushpin: Doc) => ( - // new ScriptField(undefined)} - // onKey={undefined} - // onDoubleClick={undefined} - // onBrowseClick={undefined} - // docFilters={returnEmptyDoclist} - // docRangeFilters={returnEmptyDoclist} - // searchFilterDocs={returnEmptyDoclist} - // isDocumentActive={returnFalse} - // isContentActive={returnFalse} - // addDocTab={returnFalse} - // ScreenToLocalTransform={()=>new Transform(0,0,0)} - // fitContentsToBox={undefined} - // focus={returnOne} - // />)); - - // }, {fireImmediately: false}); }; + + // Keeps track of when dragging a pin onto map + draggingPin = false; + dragToggle(){ + console.log("DRAGGING TOGGLE") + document.addEventListener('pointerup', this.dropPin) + } + dropPin = (e:PointerEvent) => { + // document.removeChild(ele); + + + let target = document.elementFromPoint(e.x, e.y); + + while (target != null){ + if (target === this._ref.current){ + const location = this.MicrosoftMaps.tryPixelToLocation(new this.MicrosoftMaps.Point(e.clientX,e.clientY)) + +console.log(location) + this.createPushpin(location.latitude, location.longitude); + + + + break; + } + target = target.parentElement + } + document.removeEventListener('pointerup', this.dropPin) + } + + + searchbarKeyDown = (e: any) => { if (e.key === 'Enter') { this.bingSearch(); @@ -799,6 +803,7 @@ export class MapBox extends ViewBoxAnnotatableComponent typeof newText === 'string' && this.searchbarOnEdit(newText)} placeholder="Boston" + // text="text" />

{this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}

- + + {/* + text="amongus" /> */} +
-
- {/* */} - - {/* {this.placePinOn ? : } */} - {/* {this.placePinOn ? : } - */} -
- {/* {this.pinIsSelected_TEMPORARY? : null} */} - + this.draggingPin=false} + + >
{!this._mapReady ? null -- cgit v1.2.3-70-g09d2 From cba0d4b26e8e5daf274c7a8ccfd130f284ce2946 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 21 Aug 2023 14:46:05 -0400 Subject: new push --- src/client/views/nodes/MapBox/MapBox.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index bd631f942..58a43c55a 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -742,11 +742,17 @@ export class MapBox extends ViewBoxAnnotatableComponent { console.log("DRAGGING TOGGLE") - document.addEventListener('pointerup', this.dropPin) + document.addEventListener('drop', this.dropPin, true) + document.addEventListener('pointermove', this.pinMove, true) + e.stopPropagation(); } - dropPin = (e:PointerEvent) => { + pinMove = (e:PointerEvent) => { + console.log("MOVING"); + e.stopPropagation(); + } + dropPin = (e:DragEvent) => { // document.removeChild(ele); @@ -759,13 +765,13 @@ export class MapBox extends ViewBoxAnnotatableComponent Date: Mon, 21 Aug 2023 15:39:49 -0400 Subject: map fixes for drag and drop --- src/client/views/nodes/MapBox/MapBox.scss | 9 +-- src/client/views/nodes/MapBox/MapBox.tsx | 107 +++++++++++++----------------- 2 files changed, 50 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss index 39fa3262e..a910f294e 100644 --- a/src/client/views/nodes/MapBox/MapBox.scss +++ b/src/client/views/nodes/MapBox/MapBox.scss @@ -11,12 +11,13 @@ padding: 12; font-size: 17; } - .mapBox-searchbar{ - display:flex; + .mapBox-searchbar { + display: flex; flex-direction: row; + width: calc(100% - 40px); } - .mapBox-topbar{ - display:flex; + .mapBox-topbar { + display: flex; flex-direction: row; } diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 58a43c55a..0eafe4312 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,7 +1,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@mui/material'; import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; -import { EditableText, IconButton, Toggle, Type } from 'browndash-components'; +import { Button, EditableText, IconButton, Toggle, Type } from 'browndash-components'; import { tickStep } from 'd3'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -697,7 +698,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this.placePinOn) this.placePinOn = false; @@ -742,39 +743,35 @@ export class MapBox extends ViewBoxAnnotatableComponent { - console.log("DRAGGING TOGGLE") - document.addEventListener('drop', this.dropPin, true) - document.addEventListener('pointermove', this.pinMove, true) - e.stopPropagation(); - } - pinMove = (e:PointerEvent) => { - console.log("MOVING"); + dragToggle = (e: React.PointerEvent) => { + console.log('DRAGGING TOGGLE'); + document.addEventListener('drop', this.dropPin, true); + document.addEventListener('pointermove', this.pinMove, true); e.stopPropagation(); - } - dropPin = (e:DragEvent) => { - // document.removeChild(ele); - - - let target = document.elementFromPoint(e.x, e.y); - - while (target != null){ - if (target === this._ref.current){ - const location = this.MicrosoftMaps.tryPixelToLocation(new this.MicrosoftMaps.Point(e.clientX,e.clientY)) - -console.log(location) + }; + pinMove = (e: PointerEvent) => { + console.log('MOVING'); + e.stopPropagation(); + }; + dropPin = (e: DragEvent) => { + e.stopPropagation(); + e.preventDefault(); + document.removeEventListener('drop', this.dropPin, true); + document.removeEventListener('pointermove', this.pinMove, true); + let target = document.elementFromPoint(e.x, e.y); + + while (target != null) { + if (target === this._ref.current) { + const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); + const x = cpt[0] - this.props.PanelWidth() / 2; + const y = cpt[1] - 32 - this.props.PanelHeight() / 2; + const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y)); this.createPushpin(location.latitude, location.longitude); - - e.stopPropagation(); - break; } - target = target.parentElement - } - document.removeEventListener('drop', this.dropPin) - } - - + target = target.parentElement; + } + }; searchbarKeyDown = (e: any) => { if (e.key === 'Enter') { @@ -805,13 +802,13 @@ console.log(location) {this.annotationLayer}
- typeof newText === 'string' && this.searchbarOnEdit(newText)} - placeholder="Boston" - - // text="text" - /> +
+ typeof newText === 'string' && this.searchbarOnEdit(newText)} + placeholder="Boston" + /> +
-- cgit v1.2.3-70-g09d2 From cb03770340e81986dd2c9a0f99f60588c1207745 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 21 Aug 2023 16:15:18 -0400 Subject: layout of search bar --- src/client/views/nodes/MapBox/MapBox.scss | 1 + src/client/views/nodes/MapBox/MapBox.tsx | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss index b704b26a2..464646a23 100644 --- a/src/client/views/nodes/MapBox/MapBox.scss +++ b/src/client/views/nodes/MapBox/MapBox.scss @@ -14,6 +14,7 @@ .mapBox-searchbar { display: flex; flex-direction: row; + width: calc(100% - 40px); .editableText-container { width: 100%; } diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 91333845a..bd6adaecb 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -799,7 +799,6 @@ export class MapBox extends ViewBoxAnnotatableComponent -
typeof newText === 'string' && this.searchbarOnEdit(newText)} @@ -807,7 +806,6 @@ export class MapBox extends ViewBoxAnnotatableComponent -