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 3c8cb517c811f94dce1e3d8430e07af316642365 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Thu, 13 Apr 2023 05:39:21 -0400 Subject: Compile and make compatible all the scattered code I had for empowering trails for dash meeting. Still much to do with ui, but basic functionaltiy is there. Two key things, 1) navigation for branching trails, and 2) ability to runSubroutines on tested trails. --- src/client/util/BranchingTrailManager.tsx | 113 ++++++++++++++++++++++ src/client/util/DocumentManager.ts | 1 + src/client/views/Main.tsx | 2 + src/client/views/MainView.tsx | 1 + src/client/views/collections/TreeView.scss | 5 +- src/client/views/collections/TreeView.tsx | 51 +++++++++- src/client/views/nodes/trails/PresBox.tsx | 20 ++++ src/client/views/nodes/trails/PresElementBox.scss | 4 + src/client/views/nodes/trails/PresElementBox.tsx | 34 ++++++- 9 files changed, 225 insertions(+), 6 deletions(-) create mode 100644 src/client/util/BranchingTrailManager.tsx (limited to 'src') diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx new file mode 100644 index 000000000..5a9dd7743 --- /dev/null +++ b/src/client/util/BranchingTrailManager.tsx @@ -0,0 +1,113 @@ +import * as React from 'react'; +import { action, observable, computed } from 'mobx'; +import { observer } from 'mobx-react'; +import { Doc } from '../../fields/Doc'; +import { PresBox } from '../views/nodes/trails'; +import { Id } from '../../fields/FieldSymbols'; +import { DocumentManager } from './DocumentManager'; +import { OverlayView } from '../views/OverlayView'; +import { compute } from 'googleapis/build/src/apis/compute'; + +@observer +export class BranchingTrailManager extends React.Component { + public static Instance: BranchingTrailManager; + + constructor(props: any) { + super(props); + if (!BranchingTrailManager.Instance) { + BranchingTrailManager.Instance = this; + } + } + + setupUi = () => { + OverlayView.Instance.addWindow(, { x: 100, y: 150, width: 1000, title: 'Branching Trail' }); + }; + + // stack of the history + @observable private slideHistoryStack: String[] = []; + @action setSlideHistoryStack = action((newArr: String[]) => { + this.slideHistoryStack = newArr; + }); + + @observable private containsSet: Set = new Set(); + + // prev pres to copmare with + @observable private prevPresId: String | null = null; + @action setPrevPres = action((newId: String | null) => { + this.prevPresId = newId; + }); + + // docId to Doc map + @observable private docIdToDocMap: Map = new Map(); + + observeDocumentChange = (targetDoc: Doc, pres: PresBox) => { + const presId = pres.props.Document[Id]; + if (this.prevPresId === presId) { + return; + } + + const targetDocId = targetDoc[Id]; + this.docIdToDocMap.set(targetDocId, targetDoc); + + if (this.prevPresId === null) { + this.setupUi(); + } + + if (this.prevPresId === null || this.prevPresId !== presId) { + this.setPrevPres(presId); + // REVERT THE SET + const stringified = [presId, targetDocId].toString(); + if (this.containsSet.has([presId, targetDocId].toString())) { + // remove all the elements after the targetDocId + const newStack = this.slideHistoryStack.slice(0, this.slideHistoryStack.indexOf(stringified)); + const removed = this.slideHistoryStack.slice(this.slideHistoryStack.indexOf(stringified)); + this.setSlideHistoryStack(newStack); + + removed.forEach(info => this.containsSet.delete(info.toString())); + } else { + this.setSlideHistoryStack([...this.slideHistoryStack, stringified]); + this.containsSet.add(stringified); + } + } + }; + + clickHandler = (e: React.PointerEventHandler, targetDocId: string, removeIndex: number) => { + const targetDoc = this.docIdToDocMap.get(targetDocId); + if (!targetDoc) { + return; + } + const newStack = this.slideHistoryStack.slice(0, removeIndex); + const removed = this.slideHistoryStack.slice(removeIndex); + this.setSlideHistoryStack(newStack); + + removed.forEach(info => this.containsSet.delete(info.toString())); + DocumentManager.Instance.showDocument(targetDoc, { willZoomCentered: true }); + //PresBox.NavigateToTarget(targetDoc, targetDoc); + }; + + @computed get trailBreadcrumbs() { + return ( +
+ {this.slideHistoryStack.map((info, index) => { + const [presId, targetDocId] = info.split(','); + const doc = this.docIdToDocMap.get(targetDocId); + if (!doc) { + return <>; + } + return ( + + + -{'>'} + + ); + })} +
+ ); + } + + render() { + return
{BranchingTrailManager.Instance.trailBreadcrumbs}
; + } +} diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ccf370662..9cf584bba 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -19,6 +19,7 @@ import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { PresBox } from '../views/nodes/trails'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; +import { BranchingTrailManager } from './BranchingTrailManager'; const { Howl } = require('howler'); export class DocumentManager { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 6b18caed0..1aeb93d42 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -15,6 +15,7 @@ import { TrackMovements } from '../util/TrackMovements'; import { CollectionView } from './collections/CollectionView'; import { MainView } from './MainView'; import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import +import { BranchingTrailManager } from '../util/BranchingTrailManager'; dotenv.config(); AssignAllExtensions(); @@ -48,6 +49,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure new LinkManager(); new TrackMovements(); new ReplayMovements(); + new BranchingTrailManager(); root.render(); }, 0); })(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 60459cf30..1fc5ee1d7 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -63,6 +63,7 @@ import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TopBar } from './topbar/TopBar'; +import { BranchingTrailManager } from '../util/BranchingTrailManager'; const _global = (window /* browser */ || global) /* node */ as any; @observer diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index 7eab03e1d..cb6432687 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -25,10 +25,11 @@ width: 100%; height: 100%; position: absolute; + left: 10px; .treeView-expandIcon { - display: none; - left: -10px; + // display: none; + left: -8px; position: absolute; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 75e76019e..153c2c71a 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -227,6 +227,37 @@ export class TreeView extends React.Component { } }; + @undoBatch + @action + recurToggle = (childList: Doc[]) => { + if (childList.length > 0) { + childList.forEach(child => { + console.log(child); + child.runProcess = !!!child.runProcess; + child.toggleChildrenRun?.(); + }); + } + }; + + @undoBatch + @action + getRunningChildren = (childList: Doc[]) => { + if (childList.length === 0) { + return []; + } + + const runningChildren = []; + childList.forEach(child => { + if (child.runProcess && child.getChildrenToRun) { + if (child.runProcess) { + runningChildren.push(child); + } + runningChildren.push(...(child.getChildrenToRun?.() ?? [])); + } + }); + return runningChildren; + }; + constructor(props: any) { super(props); if (!TreeView._openLevelScript) { @@ -235,6 +266,18 @@ export class TreeView extends React.Component { } this._openScript = Doc.IsSystem(this.props.document) ? undefined : () => TreeView._openLevelScript!; this._editTitleScript = Doc.IsSystem(this.props.document) ? () => TreeView._openLevelScript! : () => TreeView._openTitleScript!; + + // set for child processing highligting + // this.dataDoc.testing = 'testing'; + this.dataDoc.hasChildren = this.childDocs.length > 0; + // this.dataDoc.children = this.childDocs; + this.dataDoc.toggleChildrenRun = () => { + this.recurToggle(this.childDocs); + }; + + this.dataDoc.getChildrenToRun = () => { + return this.getRunningChildren(this.childDocs); + }; } _treeEle: any; @@ -696,7 +739,11 @@ export class TreeView extends React.Component { ) : (
- + = 1 ? 'block' : 'none' }} + icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} + />
{this.onCheckedClick ? null : typeof iconType === 'string' ? : iconType}
@@ -899,7 +946,7 @@ export class TreeView extends React.Component { })} Document={this.doc} fitWidth={returnTrue} - DataDoc={undefined} + DataDoc={this.dataDoc} scriptContext={this} hideDecorationTitle={this.props.treeView.outlineMode} hideResizeHandles={this.props.treeView.outlineMode} diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 3589a9065..2836a39fb 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -35,6 +35,7 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; +import { BranchingTrailManager } from '../../../util/BranchingTrailManager'; const { Howl } = require('howler'); export interface pinDataTypes { @@ -278,6 +279,19 @@ export class PresBox extends ViewBoxBaseComponent() { return listItems.filter(doc => !doc.unrendered); } }; + + // go to documents chain + runSubroutines = (childrenToRun: Doc[], normallyNextSlide: Doc) => { + console.log(childrenToRun, normallyNextSlide, 'runSUBFUNC'); + if (childrenToRun[0] === normallyNextSlide) { + return; + } + + childrenToRun.forEach(child => { + DocumentManager.Instance.showDocument(child, {}); + }); + }; + // Called when the user activates 'next' - to move to the next part of the pres. trail @action next = () => { @@ -314,6 +328,11 @@ export class PresBox extends ViewBoxBaseComponent() { // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]); const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex; + + // before moving onto next slide, run the subroutines :) + const currentDoc = this.childDocs[this.itemIndex]; + this.runSubroutines(currentDoc.getChildrenToRun?.(), this.childDocs[this.itemIndex + 1]); + this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1); progressiveReveal(true); // shows first progressive document, but without a transition effect } else { @@ -684,6 +703,7 @@ export class PresBox extends ViewBoxBaseComponent() { navigateToActiveItem = (afterNav?: () => void) => { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; + BranchingTrailManager.Instance.observeDocumentChange(targetDoc, this); const finished = () => { afterNav?.(); console.log('Finish Slide Nav: ' + targetDoc.title); diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss index 4f95f0c1f..9ac2b5a94 100644 --- a/src/client/views/nodes/trails/PresElementBox.scss +++ b/src/client/views/nodes/trails/PresElementBox.scss @@ -4,6 +4,10 @@ $light-background: #ececec; $slide-background: #d5dce2; $slide-active: #5b9fdd; +.testingv2 { + background-color: red; +} + .presItem-container { cursor: grab; display: flex; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 92696240b..96f6a514b 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -180,7 +180,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { e.stopPropagation(); e.preventDefault(); this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); - this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem); + // this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem); }); } }; @@ -403,6 +403,19 @@ export class PresElementBox extends ViewBoxBaseComponent() { } }; + @undoBatch + @action + lfg = (e: React.MouseEvent) => { + e.stopPropagation(); + console.log('lfg called'); + // TODO: fix this bug + const { toggleChildrenRun } = this.rootDoc; + toggleChildrenRun?.(); + + // call this.rootDoc.recurChildren() to get all the children + // if (iconClick) PresElementBox.showVideo = false; + }; + @computed get toolbarWidth(): number { const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox); @@ -418,6 +431,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false; const targetDoc: Doc = this.targetDoc; const activeItem: Doc = this.rootDoc; + const hasChildren: boolean = Cast(this.rootDoc?.hasChildren, 'boolean') || false; const items: JSX.Element[] = []; items.push( @@ -482,6 +496,22 @@ export class PresElementBox extends ViewBoxBaseComponent() {
); + if (!Doc.noviceMode && hasChildren) { + // TODO: replace with if treeveiw, has childrenDocs + items.push( + Run child processes (tree only)
}> +
{ + e.stopPropagation(); + this.lfg(e); + }} + style={{ fontWeight: 700 }}> + e.stopPropagation()} /> +
+ + ); + } items.push( Remove from presentation
}>
@@ -528,7 +558,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { ) : (
Date: Mon, 17 Apr 2023 15:39:41 -0400 Subject: minor cleanup --- src/client/util/BranchingTrailManager.tsx | 11 +++++------ src/client/util/DocumentManager.ts | 1 - src/client/views/MainView.tsx | 5 ++--- src/client/views/collections/TreeView.scss | 3 +-- 4 files changed, 8 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx index 5a9dd7743..44cec6922 100644 --- a/src/client/util/BranchingTrailManager.tsx +++ b/src/client/util/BranchingTrailManager.tsx @@ -1,12 +1,11 @@ -import * as React from 'react'; -import { action, observable, computed } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; +import * as React from 'react'; import { Doc } from '../../fields/Doc'; -import { PresBox } from '../views/nodes/trails'; import { Id } from '../../fields/FieldSymbols'; -import { DocumentManager } from './DocumentManager'; +import { PresBox } from '../views/nodes/trails'; import { OverlayView } from '../views/OverlayView'; -import { compute } from 'googleapis/build/src/apis/compute'; +import { DocumentManager } from './DocumentManager'; @observer export class BranchingTrailManager extends React.Component { @@ -71,7 +70,7 @@ export class BranchingTrailManager extends React.Component { } }; - clickHandler = (e: React.PointerEventHandler, targetDocId: string, removeIndex: number) => { + clickHandler = (e: React.PointerEvent, targetDocId: string, removeIndex: number) => { const targetDoc = this.docIdToDocMap.get(targetDocId); if (!targetDoc) { return; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 8263e1ba7..e01457b4f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -15,7 +15,6 @@ import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { PresBox } from '../views/nodes/trails'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; -import { BranchingTrailManager } from './BranchingTrailManager'; const { Howl } = require('howler'); export class DocumentManager { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f0f744886..f625d44fe 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -14,7 +14,7 @@ import { StrCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; -import { Docs, DocUtils } from '../documents/Documents'; +import { Docs } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { CaptureManager } from '../util/CaptureManager'; import { DocumentManager } from '../util/DocumentManager'; @@ -40,7 +40,7 @@ import { DashboardView } from './DashboardView'; import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import { GestureOverlay } from './GestureOverlay'; -import { TOPBAR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss'; +import { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } from './global/globalCssVariables.scss'; import { Colors } from './global/globalEnums'; import { KeyManager } from './GlobalKeyHandler'; import { InkTranscription } from './InkTranscription'; @@ -63,7 +63,6 @@ import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TopBar } from './topbar/TopBar'; -import { BranchingTrailManager } from '../util/BranchingTrailManager'; const _global = (window /* browser */ || global) /* node */ as any; @observer diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index cb6432687..185bed74c 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -25,10 +25,9 @@ width: 100%; height: 100%; position: absolute; - left: 10px; .treeView-expandIcon { - // display: none; + display: none; left: -8px; position: absolute; } -- 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: Wed, 14 Jun 2023 09:12:33 -0400 Subject: fixes after merge of advanced trails --- src/client/views/StyleProvider.tsx | 4 ++-- src/client/views/nodes/trails/PresBox.tsx | 3 ++- src/client/views/nodes/trails/PresElementBox.tsx | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 9199bf931..5eabf21fc 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -240,12 +240,12 @@ export function DefaultStyleProvider(doc: Opt, props: Opt() { // before moving onto next slide, run the subroutines :) const currentDoc = this.childDocs[this.itemIndex]; - this.runSubroutines(currentDoc.getChildrenToRun?.(), this.childDocs[this.itemIndex + 1]); + this.runSubroutines(TreeView.GetRunningChildren.get(currentDoc)?.(), this.childDocs[this.itemIndex + 1]); this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1); progressiveReveal(true); // shows first progressive document, but without a transition effect diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index cac6cdd04..a98db3d66 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -25,6 +25,7 @@ import { PresBox } from './PresBox'; import './PresElementBox.scss'; import { PresMovement } from './PresEnums'; import React = require('react'); +import { TreeView } from '../../collections/TreeView'; /** * This class models the view a document added to presentation will have in the presentation. * It involves some functionality for its buttons and options. @@ -178,7 +179,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { e.stopPropagation(); e.preventDefault(); this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); - // this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem); + this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem); }); } }; @@ -402,7 +403,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { console.log('lfg called'); // TODO: fix this bug const { toggleChildrenRun } = this.rootDoc; - toggleChildrenRun?.(); + TreeView.ToggleChildrenRun.get(this.rootDoc)?.(); // call this.rootDoc.recurChildren() to get all the children // if (iconClick) PresElementBox.showVideo = false; -- cgit v1.2.3-70-g09d2 From 14c57f0f70799ada841a03a150f4f759578fa69b Mon Sep 17 00:00:00 2001 From: Eric 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 bdabb0eb1aeac9ea9d4f1fa40889b8d30937c1f0 Mon Sep 17 00:00:00 2001 From: monoguitari <113245090+monoguitari@users.noreply.github.com> Date: Sat, 19 Aug 2023 03:22:16 -0400 Subject: Toggling icon for mic and added webcapturedoc to dropdown --- src/client/util/CurrentUserUtils.ts | 8 +++- src/client/views/UndoStack.tsx | 4 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 47 ++++++++++++++++++++-- .../views/nodes/RecordingBox/RecordingView.tsx | 3 +- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- 6 files changed, 56 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 25c8f511b..54828867e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -714,7 +714,9 @@ export class CurrentUserUtils { { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected - { title: "Audio", icon: "microphone", toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}, funcs: { }} + { title: "Audio", icon: 'microphone', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'},}, + { title: "StopRec", icon: "stop", toolTip: "Stop", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecording()`}, ignoreClick: true, scripts: { onClick: `return toggleRecording(_readOnly_)`}}, + { title: "Dropdown", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList, expertMode: false, funcs: {btnList: `getWorkspaceRecordings()`}, ignoreClick: true, scripts: { script: `toggleRecPlayback(value)`}, }, ]; } @@ -737,6 +739,8 @@ export class CurrentUserUtils { static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc { const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title); const subMenu = params.subMenu; + Doc.UserDoc().workspaceRecordings = new List; + Doc.UserDoc().isRecording = false; if (!subMenu) { // button does not have a sub menu return this.setupContextMenuButton(params, menuBtnDoc); } @@ -999,4 +1003,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file +ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index a551e5332..813cdd050 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -37,7 +37,7 @@ export class UndoStack extends React.Component { }}> {UndoManager.undoStackNames.map((name, i) => (
-
{name.replace(/[^\.]*\./, '')}
+
{StrCast(name).replace(/[^\.]*\./, '')}
))} {Array.from(UndoManager.redoStackNames) @@ -45,7 +45,7 @@ export class UndoStack extends React.Component { .map((name, i) => (
- {name.replace(/[^\.]*\./, '')} + {StrCast(name).replace(/[^\.]*\./, '')}
))} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 5ff5f7bfa..41ad90155 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; import { undoable, UndoManager } from '../../../util/UndoManager'; @@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent() { const list: IListItemProps[] = this.buttonList .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) .map(value => ({ - text: value.charAt(0).toUpperCase() + value.slice(1), + text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title), val: value, style: getStyle(value), onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value), diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 8fa2861b6..3237ce6cd 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -15,6 +15,8 @@ import { BoolCast, DocCast } from '../../../../fields/Types'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { DocumentManager } from '../../../util/DocumentManager'; import { Docs } from '../../../documents/Documents'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; +import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; @observer export class RecordingBox extends ViewBoxBaseComponent() { @@ -82,27 +84,64 @@ export class RecordingBox extends ViewBoxBaseComponent() { static screengrabber: RecordingBox | undefined; } ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { + console.log(_readOnly_) + console.log(RecordingBox.screengrabber) if (_readOnly_) return RecordingBox.screengrabber ? true : false; if (RecordingBox.screengrabber) { + //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening + console.log('grabbing screen!') RecordingBox.screengrabber.Pause?.(); setTimeout(() => { RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 100; + RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100 RecordingBox.screengrabber!.rootDoc.overlayY = 100; + console.log(RecordingBox.screengrabber?.rootDoc) RecordingBox.screengrabber = undefined; }, 100); + Doc.UserDoc().isRecording = false; + // console.log(RecordingBox.screengrabber.dataDoc); + // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc); + Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc); + // console.log(RecordingBox.screengrabber.rootDoc) + // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf); } else { + //when we first press mic const screengrabber = Docs.Create.WebCamDocument('', { _width: 384, _height: 216, }); - screengrabber.overlayX = -400; - screengrabber.overlayY = 0; + // Doc.UserDoc().currentScreenGrab = screengrabber; + screengrabber.overlayX = 100; //was -400 + screengrabber.overlayY = 100; //was 0 screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; - Doc.AddToMyOverlay(screengrabber); + Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { RecordingBox.screengrabber = docView.ComponentView as RecordingBox; RecordingBox.screengrabber.Record?.(); }); + // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber); + Doc.UserDoc().isRecording = true; + } }, 'toggle recording'); +ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { + console.log(value) + const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216}); + + let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); + (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); + +}); + +ScriptingGlobals.add(function getCurrRecording() { + console.log(RecordingBox.screengrabber); +}); +ScriptingGlobals.add(function getWorkspaceRecordings() { + return Doc.UserDoc().workspaceRecordings +}); +ScriptingGlobals.add(function getIsRecording() { + return Doc.UserDoc().isRecording; +}) + + + diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 0e386b093..8c0f5efef 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -163,8 +163,9 @@ export function RecordingView(props: IRecordingViewProps) { // if this is called, then we're done recording all the segments const finish = () => { // call stop on the video recorder if active + console.log(videoRecorder.current?.state); videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop(); - + console.log("this it") // end the streams (audio/video) to remove recording icon const stream = videoElementRef.current!.srcObject; stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop()); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index aa514be3b..5de51dbe9 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @undoBatch @action - startRecording = (e: React.MouseEvent, activeItem: Doc) => { + startRecording = (e: React.MouseEvent, activeItem: Doc) => { e.stopPropagation(); if (PresElementBox.videoIsRecorded(activeItem)) { // if we already have an existing recording -- cgit v1.2.3-70-g09d2 From 2466587655888d5894159b942f09661ab73ebe66 Mon Sep 17 00:00:00 2001 From: monoguitari <113245090+monoguitari@users.noreply.github.com> Date: Sat, 19 Aug 2023 08:33:14 -0400 Subject: Revert "Toggling icon for mic and added webcapturedoc to dropdown" This reverts commit bdabb0eb1aeac9ea9d4f1fa40889b8d30937c1f0. --- src/client/util/CurrentUserUtils.ts | 8 +--- src/client/views/UndoStack.tsx | 4 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 47 ++-------------------- .../views/nodes/RecordingBox/RecordingView.tsx | 3 +- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- 6 files changed, 12 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 54828867e..25c8f511b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -714,9 +714,7 @@ export class CurrentUserUtils { { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected - { title: "Audio", icon: 'microphone', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'},}, - { title: "StopRec", icon: "stop", toolTip: "Stop", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecording()`}, ignoreClick: true, scripts: { onClick: `return toggleRecording(_readOnly_)`}}, - { title: "Dropdown", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList, expertMode: false, funcs: {btnList: `getWorkspaceRecordings()`}, ignoreClick: true, scripts: { script: `toggleRecPlayback(value)`}, }, + { title: "Audio", icon: "microphone", toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}, funcs: { }} ]; } @@ -739,8 +737,6 @@ export class CurrentUserUtils { static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc { const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title); const subMenu = params.subMenu; - Doc.UserDoc().workspaceRecordings = new List; - Doc.UserDoc().isRecording = false; if (!subMenu) { // button does not have a sub menu return this.setupContextMenuButton(params, menuBtnDoc); } @@ -1003,4 +999,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); +ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index 813cdd050..a551e5332 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -37,7 +37,7 @@ export class UndoStack extends React.Component { }}> {UndoManager.undoStackNames.map((name, i) => (
-
{StrCast(name).replace(/[^\.]*\./, '')}
+
{name.replace(/[^\.]*\./, '')}
))} {Array.from(UndoManager.redoStackNames) @@ -45,7 +45,7 @@ export class UndoStack extends React.Component { .map((name, i) => (
- {StrCast(name).replace(/[^\.]*\./, '')} + {name.replace(/[^\.]*\./, '')}
))} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 41ad90155..5ff5f7bfa 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; import { undoable, UndoManager } from '../../../util/UndoManager'; @@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent() { const list: IListItemProps[] = this.buttonList .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) .map(value => ({ - text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title), + text: value.charAt(0).toUpperCase() + value.slice(1), val: value, style: getStyle(value), onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value), diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 3237ce6cd..8fa2861b6 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -15,8 +15,6 @@ import { BoolCast, DocCast } from '../../../../fields/Types'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { DocumentManager } from '../../../util/DocumentManager'; import { Docs } from '../../../documents/Documents'; -import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; -import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; @observer export class RecordingBox extends ViewBoxBaseComponent() { @@ -84,64 +82,27 @@ export class RecordingBox extends ViewBoxBaseComponent() { static screengrabber: RecordingBox | undefined; } ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { - console.log(_readOnly_) - console.log(RecordingBox.screengrabber) if (_readOnly_) return RecordingBox.screengrabber ? true : false; if (RecordingBox.screengrabber) { - //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening - console.log('grabbing screen!') RecordingBox.screengrabber.Pause?.(); setTimeout(() => { RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100 + RecordingBox.screengrabber!.rootDoc.overlayX = 100; RecordingBox.screengrabber!.rootDoc.overlayY = 100; - console.log(RecordingBox.screengrabber?.rootDoc) RecordingBox.screengrabber = undefined; }, 100); - Doc.UserDoc().isRecording = false; - // console.log(RecordingBox.screengrabber.dataDoc); - // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc); - Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc); - // console.log(RecordingBox.screengrabber.rootDoc) - // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf); } else { - //when we first press mic const screengrabber = Docs.Create.WebCamDocument('', { _width: 384, _height: 216, }); - // Doc.UserDoc().currentScreenGrab = screengrabber; - screengrabber.overlayX = 100; //was -400 - screengrabber.overlayY = 100; //was 0 + screengrabber.overlayX = -400; + screengrabber.overlayY = 0; screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; - Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay + Doc.AddToMyOverlay(screengrabber); DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { RecordingBox.screengrabber = docView.ComponentView as RecordingBox; RecordingBox.screengrabber.Record?.(); }); - // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber); - Doc.UserDoc().isRecording = true; - } }, 'toggle recording'); -ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { - console.log(value) - const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216}); - - let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); - (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); - -}); - -ScriptingGlobals.add(function getCurrRecording() { - console.log(RecordingBox.screengrabber); -}); -ScriptingGlobals.add(function getWorkspaceRecordings() { - return Doc.UserDoc().workspaceRecordings -}); -ScriptingGlobals.add(function getIsRecording() { - return Doc.UserDoc().isRecording; -}) - - - diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 8c0f5efef..0e386b093 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -163,9 +163,8 @@ export function RecordingView(props: IRecordingViewProps) { // if this is called, then we're done recording all the segments const finish = () => { // call stop on the video recorder if active - console.log(videoRecorder.current?.state); videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop(); - console.log("this it") + // end the streams (audio/video) to remove recording icon const stream = videoElementRef.current!.srcObject; stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop()); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 5de51dbe9..aa514be3b 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @undoBatch @action - startRecording = (e: React.MouseEvent, activeItem: Doc) => { + startRecording = (e: React.MouseEvent, activeItem: Doc) => { e.stopPropagation(); if (PresElementBox.videoIsRecorded(activeItem)) { // if we already have an existing recording -- cgit v1.2.3-70-g09d2 From 5e8cc0a6a7f8dfc0c33fbdfa8879de88f057233e Mon Sep 17 00:00:00 2001 From: monoguitari <113245090+monoguitari@users.noreply.github.com> Date: Sat, 19 Aug 2023 08:39:45 -0400 Subject: Revert "Revert "Toggling icon for mic and added webcapturedoc to dropdown"" This reverts commit 2466587655888d5894159b942f09661ab73ebe66. --- src/client/util/CurrentUserUtils.ts | 8 +++- src/client/views/UndoStack.tsx | 4 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 47 ++++++++++++++++++++-- .../views/nodes/RecordingBox/RecordingView.tsx | 3 +- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- 6 files changed, 56 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 25c8f511b..54828867e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -714,7 +714,9 @@ export class CurrentUserUtils { { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected - { title: "Audio", icon: "microphone", toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}, funcs: { }} + { title: "Audio", icon: 'microphone', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'},}, + { title: "StopRec", icon: "stop", toolTip: "Stop", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecording()`}, ignoreClick: true, scripts: { onClick: `return toggleRecording(_readOnly_)`}}, + { title: "Dropdown", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList, expertMode: false, funcs: {btnList: `getWorkspaceRecordings()`}, ignoreClick: true, scripts: { script: `toggleRecPlayback(value)`}, }, ]; } @@ -737,6 +739,8 @@ export class CurrentUserUtils { static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc { const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title); const subMenu = params.subMenu; + Doc.UserDoc().workspaceRecordings = new List; + Doc.UserDoc().isRecording = false; if (!subMenu) { // button does not have a sub menu return this.setupContextMenuButton(params, menuBtnDoc); } @@ -999,4 +1003,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file +ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index a551e5332..813cdd050 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -37,7 +37,7 @@ export class UndoStack extends React.Component { }}> {UndoManager.undoStackNames.map((name, i) => (
-
{name.replace(/[^\.]*\./, '')}
+
{StrCast(name).replace(/[^\.]*\./, '')}
))} {Array.from(UndoManager.redoStackNames) @@ -45,7 +45,7 @@ export class UndoStack extends React.Component { .map((name, i) => (
- {name.replace(/[^\.]*\./, '')} + {StrCast(name).replace(/[^\.]*\./, '')}
))} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 5ff5f7bfa..41ad90155 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; import { undoable, UndoManager } from '../../../util/UndoManager'; @@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent() { const list: IListItemProps[] = this.buttonList .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) .map(value => ({ - text: value.charAt(0).toUpperCase() + value.slice(1), + text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title), val: value, style: getStyle(value), onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value), diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 8fa2861b6..3237ce6cd 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -15,6 +15,8 @@ import { BoolCast, DocCast } from '../../../../fields/Types'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { DocumentManager } from '../../../util/DocumentManager'; import { Docs } from '../../../documents/Documents'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; +import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; @observer export class RecordingBox extends ViewBoxBaseComponent() { @@ -82,27 +84,64 @@ export class RecordingBox extends ViewBoxBaseComponent() { static screengrabber: RecordingBox | undefined; } ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { + console.log(_readOnly_) + console.log(RecordingBox.screengrabber) if (_readOnly_) return RecordingBox.screengrabber ? true : false; if (RecordingBox.screengrabber) { + //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening + console.log('grabbing screen!') RecordingBox.screengrabber.Pause?.(); setTimeout(() => { RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 100; + RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100 RecordingBox.screengrabber!.rootDoc.overlayY = 100; + console.log(RecordingBox.screengrabber?.rootDoc) RecordingBox.screengrabber = undefined; }, 100); + Doc.UserDoc().isRecording = false; + // console.log(RecordingBox.screengrabber.dataDoc); + // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc); + Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc); + // console.log(RecordingBox.screengrabber.rootDoc) + // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf); } else { + //when we first press mic const screengrabber = Docs.Create.WebCamDocument('', { _width: 384, _height: 216, }); - screengrabber.overlayX = -400; - screengrabber.overlayY = 0; + // Doc.UserDoc().currentScreenGrab = screengrabber; + screengrabber.overlayX = 100; //was -400 + screengrabber.overlayY = 100; //was 0 screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; - Doc.AddToMyOverlay(screengrabber); + Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { RecordingBox.screengrabber = docView.ComponentView as RecordingBox; RecordingBox.screengrabber.Record?.(); }); + // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber); + Doc.UserDoc().isRecording = true; + } }, 'toggle recording'); +ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { + console.log(value) + const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216}); + + let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); + (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); + +}); + +ScriptingGlobals.add(function getCurrRecording() { + console.log(RecordingBox.screengrabber); +}); +ScriptingGlobals.add(function getWorkspaceRecordings() { + return Doc.UserDoc().workspaceRecordings +}); +ScriptingGlobals.add(function getIsRecording() { + return Doc.UserDoc().isRecording; +}) + + + diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 0e386b093..8c0f5efef 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -163,8 +163,9 @@ export function RecordingView(props: IRecordingViewProps) { // if this is called, then we're done recording all the segments const finish = () => { // call stop on the video recorder if active + console.log(videoRecorder.current?.state); videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop(); - + console.log("this it") // end the streams (audio/video) to remove recording icon const stream = videoElementRef.current!.srcObject; stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop()); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index aa514be3b..5de51dbe9 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @undoBatch @action - startRecording = (e: React.MouseEvent, activeItem: Doc) => { + startRecording = (e: React.MouseEvent, activeItem: Doc) => { e.stopPropagation(); if (PresElementBox.videoIsRecorded(activeItem)) { // if we already have an existing recording -- cgit v1.2.3-70-g09d2 From 40a39db25646b513c25554a2abdf35ef977500a8 Mon Sep 17 00:00:00 2001 From: monoguitari <113245090+monoguitari@users.noreply.github.com> Date: Sat, 19 Aug 2023 08:52:33 -0400 Subject: Revert "Revert "Revert "Toggling icon for mic and added webcapturedoc to dropdown""" This reverts commit 5e8cc0a6a7f8dfc0c33fbdfa8879de88f057233e. --- src/client/util/CurrentUserUtils.ts | 8 +--- src/client/views/UndoStack.tsx | 4 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 47 ++-------------------- .../views/nodes/RecordingBox/RecordingView.tsx | 3 +- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- 6 files changed, 12 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 54828867e..25c8f511b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -714,9 +714,7 @@ export class CurrentUserUtils { { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected - { title: "Audio", icon: 'microphone', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'},}, - { title: "StopRec", icon: "stop", toolTip: "Stop", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecording()`}, ignoreClick: true, scripts: { onClick: `return toggleRecording(_readOnly_)`}}, - { title: "Dropdown", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList, expertMode: false, funcs: {btnList: `getWorkspaceRecordings()`}, ignoreClick: true, scripts: { script: `toggleRecPlayback(value)`}, }, + { title: "Audio", icon: "microphone", toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}, funcs: { }} ]; } @@ -739,8 +737,6 @@ export class CurrentUserUtils { static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc { const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title); const subMenu = params.subMenu; - Doc.UserDoc().workspaceRecordings = new List; - Doc.UserDoc().isRecording = false; if (!subMenu) { // button does not have a sub menu return this.setupContextMenuButton(params, menuBtnDoc); } @@ -1003,4 +999,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); +ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index 813cdd050..a551e5332 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -37,7 +37,7 @@ export class UndoStack extends React.Component { }}> {UndoManager.undoStackNames.map((name, i) => (
-
{StrCast(name).replace(/[^\.]*\./, '')}
+
{name.replace(/[^\.]*\./, '')}
))} {Array.from(UndoManager.redoStackNames) @@ -45,7 +45,7 @@ export class UndoStack extends React.Component { .map((name, i) => (
- {StrCast(name).replace(/[^\.]*\./, '')} + {name.replace(/[^\.]*\./, '')}
))} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 41ad90155..5ff5f7bfa 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; import { undoable, UndoManager } from '../../../util/UndoManager'; @@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent() { const list: IListItemProps[] = this.buttonList .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) .map(value => ({ - text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title), + text: value.charAt(0).toUpperCase() + value.slice(1), val: value, style: getStyle(value), onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value), diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 3237ce6cd..8fa2861b6 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -15,8 +15,6 @@ import { BoolCast, DocCast } from '../../../../fields/Types'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { DocumentManager } from '../../../util/DocumentManager'; import { Docs } from '../../../documents/Documents'; -import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; -import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; @observer export class RecordingBox extends ViewBoxBaseComponent() { @@ -84,64 +82,27 @@ export class RecordingBox extends ViewBoxBaseComponent() { static screengrabber: RecordingBox | undefined; } ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { - console.log(_readOnly_) - console.log(RecordingBox.screengrabber) if (_readOnly_) return RecordingBox.screengrabber ? true : false; if (RecordingBox.screengrabber) { - //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening - console.log('grabbing screen!') RecordingBox.screengrabber.Pause?.(); setTimeout(() => { RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100 + RecordingBox.screengrabber!.rootDoc.overlayX = 100; RecordingBox.screengrabber!.rootDoc.overlayY = 100; - console.log(RecordingBox.screengrabber?.rootDoc) RecordingBox.screengrabber = undefined; }, 100); - Doc.UserDoc().isRecording = false; - // console.log(RecordingBox.screengrabber.dataDoc); - // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc); - Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc); - // console.log(RecordingBox.screengrabber.rootDoc) - // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf); } else { - //when we first press mic const screengrabber = Docs.Create.WebCamDocument('', { _width: 384, _height: 216, }); - // Doc.UserDoc().currentScreenGrab = screengrabber; - screengrabber.overlayX = 100; //was -400 - screengrabber.overlayY = 100; //was 0 + screengrabber.overlayX = -400; + screengrabber.overlayY = 0; screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; - Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay + Doc.AddToMyOverlay(screengrabber); DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { RecordingBox.screengrabber = docView.ComponentView as RecordingBox; RecordingBox.screengrabber.Record?.(); }); - // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber); - Doc.UserDoc().isRecording = true; - } }, 'toggle recording'); -ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { - console.log(value) - const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216}); - - let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); - (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); - -}); - -ScriptingGlobals.add(function getCurrRecording() { - console.log(RecordingBox.screengrabber); -}); -ScriptingGlobals.add(function getWorkspaceRecordings() { - return Doc.UserDoc().workspaceRecordings -}); -ScriptingGlobals.add(function getIsRecording() { - return Doc.UserDoc().isRecording; -}) - - - diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 8c0f5efef..0e386b093 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -163,9 +163,8 @@ export function RecordingView(props: IRecordingViewProps) { // if this is called, then we're done recording all the segments const finish = () => { // call stop on the video recorder if active - console.log(videoRecorder.current?.state); videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop(); - console.log("this it") + // end the streams (audio/video) to remove recording icon const stream = videoElementRef.current!.srcObject; stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop()); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 5de51dbe9..aa514be3b 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @undoBatch @action - startRecording = (e: React.MouseEvent, activeItem: Doc) => { + startRecording = (e: React.MouseEvent, activeItem: Doc) => { e.stopPropagation(); if (PresElementBox.videoIsRecorded(activeItem)) { // if we already have an existing recording -- cgit v1.2.3-70-g09d2 From ec6a5f5d5684ca66cd21bb8fef91c86852dd126f Mon Sep 17 00:00:00 2001 From: monoguitari <113245090+monoguitari@users.noreply.github.com> Date: Sat, 19 Aug 2023 08:53:11 -0400 Subject: Revert "Revert "Revert "Revert "Toggling icon for mic and added webcapturedoc to dropdown"""" This reverts commit 40a39db25646b513c25554a2abdf35ef977500a8. --- src/client/util/CurrentUserUtils.ts | 8 +++- src/client/views/UndoStack.tsx | 4 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 47 ++++++++++++++++++++-- .../views/nodes/RecordingBox/RecordingView.tsx | 3 +- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- 6 files changed, 56 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 25c8f511b..54828867e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -714,7 +714,9 @@ export class CurrentUserUtils { { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected - { title: "Audio", icon: "microphone", toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}, funcs: { }} + { title: "Audio", icon: 'microphone', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'},}, + { title: "StopRec", icon: "stop", toolTip: "Stop", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecording()`}, ignoreClick: true, scripts: { onClick: `return toggleRecording(_readOnly_)`}}, + { title: "Dropdown", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList, expertMode: false, funcs: {btnList: `getWorkspaceRecordings()`}, ignoreClick: true, scripts: { script: `toggleRecPlayback(value)`}, }, ]; } @@ -737,6 +739,8 @@ export class CurrentUserUtils { static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc { const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title); const subMenu = params.subMenu; + Doc.UserDoc().workspaceRecordings = new List; + Doc.UserDoc().isRecording = false; if (!subMenu) { // button does not have a sub menu return this.setupContextMenuButton(params, menuBtnDoc); } @@ -999,4 +1003,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file +ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index a551e5332..813cdd050 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -37,7 +37,7 @@ export class UndoStack extends React.Component { }}> {UndoManager.undoStackNames.map((name, i) => (
-
{name.replace(/[^\.]*\./, '')}
+
{StrCast(name).replace(/[^\.]*\./, '')}
))} {Array.from(UndoManager.redoStackNames) @@ -45,7 +45,7 @@ export class UndoStack extends React.Component { .map((name, i) => (
- {name.replace(/[^\.]*\./, '')} + {StrCast(name).replace(/[^\.]*\./, '')}
))} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 5ff5f7bfa..41ad90155 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; import { undoable, UndoManager } from '../../../util/UndoManager'; @@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent() { const list: IListItemProps[] = this.buttonList .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) .map(value => ({ - text: value.charAt(0).toUpperCase() + value.slice(1), + text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title), val: value, style: getStyle(value), onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value), diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 8fa2861b6..3237ce6cd 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -15,6 +15,8 @@ import { BoolCast, DocCast } from '../../../../fields/Types'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { DocumentManager } from '../../../util/DocumentManager'; import { Docs } from '../../../documents/Documents'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; +import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; @observer export class RecordingBox extends ViewBoxBaseComponent() { @@ -82,27 +84,64 @@ export class RecordingBox extends ViewBoxBaseComponent() { static screengrabber: RecordingBox | undefined; } ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { + console.log(_readOnly_) + console.log(RecordingBox.screengrabber) if (_readOnly_) return RecordingBox.screengrabber ? true : false; if (RecordingBox.screengrabber) { + //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening + console.log('grabbing screen!') RecordingBox.screengrabber.Pause?.(); setTimeout(() => { RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 100; + RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100 RecordingBox.screengrabber!.rootDoc.overlayY = 100; + console.log(RecordingBox.screengrabber?.rootDoc) RecordingBox.screengrabber = undefined; }, 100); + Doc.UserDoc().isRecording = false; + // console.log(RecordingBox.screengrabber.dataDoc); + // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc); + Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc); + // console.log(RecordingBox.screengrabber.rootDoc) + // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf); } else { + //when we first press mic const screengrabber = Docs.Create.WebCamDocument('', { _width: 384, _height: 216, }); - screengrabber.overlayX = -400; - screengrabber.overlayY = 0; + // Doc.UserDoc().currentScreenGrab = screengrabber; + screengrabber.overlayX = 100; //was -400 + screengrabber.overlayY = 100; //was 0 screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; - Doc.AddToMyOverlay(screengrabber); + Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { RecordingBox.screengrabber = docView.ComponentView as RecordingBox; RecordingBox.screengrabber.Record?.(); }); + // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber); + Doc.UserDoc().isRecording = true; + } }, 'toggle recording'); +ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { + console.log(value) + const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216}); + + let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); + (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); + +}); + +ScriptingGlobals.add(function getCurrRecording() { + console.log(RecordingBox.screengrabber); +}); +ScriptingGlobals.add(function getWorkspaceRecordings() { + return Doc.UserDoc().workspaceRecordings +}); +ScriptingGlobals.add(function getIsRecording() { + return Doc.UserDoc().isRecording; +}) + + + diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 0e386b093..8c0f5efef 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -163,8 +163,9 @@ export function RecordingView(props: IRecordingViewProps) { // if this is called, then we're done recording all the segments const finish = () => { // call stop on the video recorder if active + console.log(videoRecorder.current?.state); videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop(); - + console.log("this it") // end the streams (audio/video) to remove recording icon const stream = videoElementRef.current!.srcObject; stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop()); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index aa514be3b..5de51dbe9 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @undoBatch @action - startRecording = (e: React.MouseEvent, activeItem: Doc) => { + startRecording = (e: React.MouseEvent, activeItem: Doc) => { e.stopPropagation(); if (PresElementBox.videoIsRecorded(activeItem)) { // if we already have an existing recording -- 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 -
}>
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))} - style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> - C + className="slideButton" + style={{fontWeight: 700, display: 'flex'}} + > + , tooltip: "Save data to presentation", val: 'floppy', + onPointerDown: e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))}, + {icon: , tooltip: "Continously update content", val: "revert", + onPointerDown: e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.revertToPreviouslySaved(targetDoc, activeItem))}, + ]} />
- ); + ) items.push( {this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}
}>
(this.recordingIsInOverlay ? this.hideRecording(e, true) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}> -- cgit v1.2.3-70-g09d2 From 0686d497275e6deae983fcfd821495d0d126204f Mon Sep 17 00:00:00 2001 From: monoguitari <113245090+monoguitari@users.noreply.github.com> Date: Thu, 24 Aug 2023 08:06:46 -0400 Subject: overlay ui changes --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/nodes/RecordingBox/RecordingBox.tsx | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6cdc2956c..919c56fe2 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -715,7 +715,7 @@ export class CurrentUserUtils { { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected - { title: "Audio", icon: 'microphone', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}}, + { title: "Audio", icon: 'video', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}}, { title: "StopRec", icon: "stop", toolTip: "Stop", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecording()`}, ignoreClick: true, scripts: { onClick: `return toggleRecording(_readOnly_)`}}, { title: "Dropdown", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList, expertMode: false, funcs: {btnList: `getWorkspaceRecordings()`}, ignoreClick: true, scripts: { script: `toggleRecPlayback(value)`}}, { title: "Play Rec",icon: "play", toolTip: "Play recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsWorkspaceRecPlaying()`}, ignoreClick: true, scripts: { onClick: `return playWorkspaceRec(getCurrentRecording())`}}, diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index ade8d0d45..1908b74b5 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -103,8 +103,8 @@ ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { const remDoc = RecordingBox.screengrabber.rootDoc; setTimeout(() => { RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100 - RecordingBox.screengrabber!.rootDoc.overlayY = 100; + RecordingBox.screengrabber!.rootDoc.overlayX = 360; //was 100 + RecordingBox.screengrabber!.rootDoc.overlayY = 740; // DocListCast(Doc.MyOverlayDocs.data) // .filter(doc => doc.slides === RecordingBox.screengrabber!.rootDoc) // .forEach(Doc.RemFromMyOverlay); @@ -126,12 +126,12 @@ ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { } else { //when we first press mic const screengrabber = Docs.Create.WebCamDocument('', { - _width: 384, - _height: 216, + _width: 192, + _height: 108, }); // Doc.UserDoc().currentScreenGrab = screengrabber; - screengrabber.overlayX = 100; //was -400 - screengrabber.overlayY = 100; //was 0 + screengrabber.overlayX = 360; //was -400 + screengrabber.overlayY = 740; //was 0 screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { @@ -147,8 +147,8 @@ ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { let docval = undefined; Doc.UserDoc().currentRecording = value; console.log(value) - value.overlayX = 100; - value.overlayY = 100; + value.overlayX =70; + value.overlayY = 690; if (!Doc.UserDoc().isAddRecToDocMode) { Doc.UserDoc().isRecPlayback = true; Doc.UserDoc().isWorkspaceRecPlaying = true; -- cgit v1.2.3-70-g09d2 From 049cb46931301ba30ab19c95c607c42ca3198d06 Mon Sep 17 00:00:00 2001 From: monoguitari <113245090+monoguitari@users.noreply.github.com> Date: Thu, 24 Aug 2023 08:38:22 -0400 Subject: update overlay x/y again --- src/client/views/nodes/RecordingBox/RecordingBox.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 1908b74b5..429fc60c2 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -103,8 +103,8 @@ ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { const remDoc = RecordingBox.screengrabber.rootDoc; setTimeout(() => { RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 360; //was 100 - RecordingBox.screengrabber!.rootDoc.overlayY = 740; + RecordingBox.screengrabber!.rootDoc.overlayX = 70; //was 100 + RecordingBox.screengrabber!.rootDoc.overlayY = 610; // DocListCast(Doc.MyOverlayDocs.data) // .filter(doc => doc.slides === RecordingBox.screengrabber!.rootDoc) // .forEach(Doc.RemFromMyOverlay); @@ -126,12 +126,12 @@ ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { } else { //when we first press mic const screengrabber = Docs.Create.WebCamDocument('', { - _width: 192, - _height: 108, + _width: 205, + _height: 115, }); // Doc.UserDoc().currentScreenGrab = screengrabber; - screengrabber.overlayX = 360; //was -400 - screengrabber.overlayY = 740; //was 0 + screengrabber.overlayX = 70; //was -400 + screengrabber.overlayY = 610; //was 0 screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { @@ -148,7 +148,7 @@ ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { Doc.UserDoc().currentRecording = value; console.log(value) value.overlayX =70; - value.overlayY = 690; + value.overlayY = 610; if (!Doc.UserDoc().isAddRecToDocMode) { Doc.UserDoc().isRecPlayback = true; Doc.UserDoc().isWorkspaceRecPlaying = true; -- cgit v1.2.3-70-g09d2 From 6a85c8d4115a968f563369516d38549745e76303 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 24 Aug 2023 12:47:57 -0400 Subject: All working except createnote undo, and center on selected pin getAnchor --- src/client/views/SidebarAnnos.tsx | 5 ++- src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 47 ++++++++++++++++++------- src/fields/Doc.ts | 4 +-- 4 files changed, 40 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index cd50865fb..7635d719e 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -225,10 +225,9 @@ export class SidebarAnnos extends React.Component {
e.stopPropagation()}> {this.allUsers.map(renderUsers)} {this.allHashtags.map(renderTag)} - {Array.from(this.allMetadata.keys()) + {/* {Array.from(this.allMetadata.keys()) .sort() - .map(key => renderMeta(key, this.allMetadata.get(key)))} - Hello + .map(key => renderMeta(key, this.allMetadata.get(key)))} */}
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx index 439c1f14f..897bb90a6 100644 --- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx @@ -120,7 +120,7 @@ export class MapAnchorMenu extends AntimodeMenu { } + icon={} color={StrCast(Doc.UserDoc().userColor)} /> )} diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 654d446a2..dbb38e763 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -14,6 +14,7 @@ import { ScriptField } from '../../../../fields/ScriptField'; import { NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; +import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; @@ -352,10 +353,16 @@ export class MapBox extends ViewBoxAnnotatableComponent { + createNoteAnnotation = undoable(() => { !this.layoutDoc.layout_showSidebar && this.toggleSidebar(); - setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false))); // give time for sidebarRef to be created - }; + setTimeout(() =>{ + const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); + if (note && this.selectedPin) { + note.latitude = this.selectedPin.latitude; + note.longitude = this.selectedPin.latitude; + } + }); // give time for sidebarRef to be created + }, "create linked note"); sidebarDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); @@ -488,7 +495,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { + createPushpin = undoable((latitude: number, longitude: number) => { // Stores the pushpin as a MapMarkerDocument const mapMarker = Docs.Create.PushpinDocument( NumCast(latitude), @@ -501,7 +508,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this.selectedPin) { + // Removes filter + Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "remove"); + Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.longitude, "remove"); + const temp = this.selectedPin; this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp)); const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude, temp.longitude)); @@ -527,10 +538,15 @@ export class MapBox extends ViewBoxAnnotatableComponent this._sidebarRef.current?.makeDocUnfiltered(this._sidebarRef.current.)); + } + }; + getView = async (doc: Doc) => { + if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(); + return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); + }; /* * Pushpin onclick */ @@ -539,6 +555,9 @@ export class MapBox extends ViewBoxAnnotatableComponent creates a pushpin + * Map OnClick */ @action mapOnClick = (e: { location: { latitude: any; longitude: any } }) => { @@ -700,11 +718,16 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this.selectedPin) { + // Removes filter + Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "remove"); + Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.longitude, "remove"); + this.removePushpin(this.selectedPin); } MapAnchorMenu.Instance.fadeOut(true); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 6d4f4180d..7ba4f0e6f 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1490,8 +1490,8 @@ export namespace Doc { runInAction(() => { for (let i = 0; i < childFilters.length; i++) { const fields = childFilters[i].split(FilterSep); // split key:value:modifier - if (fields[0] === key && (fields[1] === value || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) { - if (fields[2] === modifiers && modifiers && fields[1] === value) { + if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) { + if (fields[2] === modifiers && modifiers && fields[1] === value.toString()) { if (toggle) modifiers = 'remove'; else return; } -- cgit v1.2.3-70-g09d2 From 10bb2ae3171c2035163c611de966c5aee4769ac3 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 24 Aug 2023 12:57:22 -0400 Subject: note icon for mapanchormenu changed --- src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx index 897bb90a6..f731763af 100644 --- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx @@ -112,7 +112,7 @@ export class MapAnchorMenu extends AntimodeMenu { } + icon={} color={StrCast(Doc.UserDoc().userColor)} /> )} -- cgit v1.2.3-70-g09d2 From bd69df995a69a72858c7a0aae6a728f54f9a5bb8 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Thu, 24 Aug 2023 12:57:49 -0400 Subject: 0 doesn't show in pie charts --- src/.DS_Store | Bin 10244 -> 10244 bytes .../views/nodes/DataVizBox/components/PieChart.tsx | 1 + 2 files changed, 1 insertion(+) (limited to 'src') diff --git a/src/.DS_Store b/src/.DS_Store index 06389d6ae..3005fd3b3 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 6d8302b6a..524de1460 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -317,6 +317,7 @@ export class PieChart extends React.Component { dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]; trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1; } + if (dataPoint[percentField] && dataPoint[percentField]==0) return ''; return dataPoint ? dataPoint[percentField]! + (!descriptionField ? '' : ' - ' + dataPoint[descriptionField])! : ''; }); }; -- cgit v1.2.3-70-g09d2 From 2fcad5e8bd1412487b4fcacefdc12a2df5707638 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Thu, 24 Aug 2023 20:12:24 +0200 Subject: filter - trying to pull --- src/client/documents/Documents.ts | 135 ++++++++++++++++++--------------- src/client/util/.ClientUtils.ts.icloud | Bin 161 -> 0 bytes src/client/views/FilterPanel.tsx | 30 ++++++-- 3 files changed, 99 insertions(+), 66 deletions(-) delete mode 100644 src/client/util/.ClientUtils.ts.icloud (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 533df5c11..1988c6328 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -86,13 +86,18 @@ export class FInfo { class BoolInfo extends FInfo { fieldType? = 'boolean'; values?: boolean[] = [true, false]; + constructor(d: string, filterable?: boolean) { + super(d); + this.filterable = filterable; + } } class NumInfo extends FInfo { fieldType? = 'number'; values?: number[] = []; - constructor(d: string, readOnly?: boolean, values?: number[], filterable?: boolean) { + constructor(d: string, filterable?: boolean, readOnly?: boolean, values?: number[]) { super(d, readOnly); this.values = values; + this.filterable = filterable; } } class StrInfo extends FInfo { @@ -110,27 +115,32 @@ class DocInfo extends FInfo { constructor(d: string, filterable?: boolean, values?: Doc[]) { super(d, true); this.values = values; + this.filterable = filterable; } } class DimInfo extends FInfo { fieldType? = 'enumeration'; values? = [DimUnit.Pixel, DimUnit.Ratio]; readOnly = false; + filterable = false; } class PEInfo extends FInfo { fieldType? = 'enumeration'; values? = ['all', 'none']; readOnly = false; + filterable = false; } class DAInfo extends FInfo { fieldType? = 'enumeration'; values? = ['embed', 'copy', 'move', 'same', 'proto', 'none']; readOnly = false; + filterable = false; } class CTypeInfo extends FInfo { fieldType? = 'enumeration'; values? = Array.from(Object.keys(CollectionViewType)); readOnly = false; + filterable = false; } class DTypeInfo extends FInfo { fieldType? = 'enumeration'; @@ -140,6 +150,7 @@ class DTypeInfo extends FInfo { class DateInfo extends FInfo { fieldType? = 'date'; values?: DateField[] = []; + filterable = true; } class ListInfo extends FInfo { fieldType? = 'list'; @@ -158,23 +169,23 @@ type DATEt = DateInfo | number; type DTYPEt = DTypeInfo | string; export class DocumentOptions { // coordinate and dimensions depending on view - x?: NUMt = new NumInfo('x coordinate of document in a freeform view'); - y?: NUMt = new NumInfo('y coordinage of document in a freeform view'); - z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, [1, 0]); - _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height"); + x?: NUMt = new NumInfo('x coordinate of document in a freeform view', false); + y?: NUMt = new NumInfo('y coordinage of document in a freeform view', false); + z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, false, [1, 0]); + _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", false); _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); - lat?: NUMt = new NumInfo('latitude coordinate for map views'); - lng?: NUMt = new NumInfo('longitude coordinate for map views'); - _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)'); - _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden'); - _width?: NUMt = new NumInfo('displayed width of a document'); - _height?: NUMt = new NumInfo('displayed height of document'); - data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)'); - data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)'); - linearBtnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)'); - _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)'); - _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)'); - _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers'); + lat?: NUMt = new NumInfo('latitude coordinate for map views', false); + lng?: NUMt = new NumInfo('longitude coordinate for map views', false); + _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)', false); + _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden', false); + _width?: NUMt = new NumInfo('displayed width of a document', true); + _height?: NUMt = new NumInfo('displayed height of document', true); + data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)', false); + data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)', false); + linearBtnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)', false); + _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)', false); + _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)', false); + _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false); _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers'); 'acl-Guest'?: string; // public permissions @@ -182,29 +193,29 @@ export class DocumentOptions { type?: DTYPEt = new DTypeInfo('type of document', true); type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection _type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection - title?: STRt = new StrInfo('title of document'); + title?: STRt = new StrInfo('title of document', true); 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', false); - 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'); - viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters'); - dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc'); + color?: STRt = new StrInfo('foreground color data doc', false); + hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection', false); + backgroundColor?: STRt = new StrInfo('background color for data doc', false); + opacity?: NUMt = new NumInfo('document opacity', false); + viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters', false); + dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc', false); _undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)' undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)' - _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title'); - _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes'); + _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title', false); + _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes', false); _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume'); _lockedPosition?: BOOLt = new BoolInfo("lock the x,y coordinates of the document so that it can't be dragged"); _lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed'); layout?: string | Doc; // default layout string or template document - layout_keyValue?: STRt = new StrInfo('layout definition for showing keyValue view of document'); - layout_explainer?: STRt = new StrInfo('explanation displayed at top of a collection to describe its purpose'); - layout_headerButton?: DOCt = new DocInfo('the (button) Doc to display at the top of a collection.'); + layout_keyValue?: STRt = new StrInfo('layout definition for showing keyValue view of document', false); + layout_explainer?: STRt = new StrInfo('explanation displayed at top of a collection to describe its purpose', false); + layout_headerButton?: DOCt = new DocInfo('the (button) Doc to display at the top of a collection.', false); layout_disableBrushing?: BOOLt = new BoolInfo('whether to suppress border highlighting'); layout_unrendered?: BOOLt = new BoolInfo('denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)'); layout_hideOpenButton?: BOOLt = new BoolInfo('whether to hide the open full screen button when selected'); @@ -216,33 +227,33 @@ export class DocumentOptions { layout_hideDecorationTitle?: BOOLt = new BoolInfo('whether to suppress the document decortations title when selected'); layout_borderRounding?: string; layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow - layout_maxAutoHeight?: NUMt = new NumInfo('maximum height for newly created (eg, from pasting) text documents'); + layout_maxAutoHeight?: NUMt = new NumInfo('maximum height for newly created (eg, from pasting) text documents', false); _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents'); - _layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document'); - _layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video) value is in seconds'); + _layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document', false); + _layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video) value is in seconds', false); _layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown'); _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)'); _layout_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content'); - _layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition'); + _layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition', false); _layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button'); _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix) _layout_showSidebar?: BOOLt = new BoolInfo('whether an annotationsidebar should be displayed for text docuemnts'); _layout_showCaption?: string; // which field to display in the caption area. leave empty to have no caption _chromeHidden?: BOOLt = new BoolInfo('whether the editing chrome for a document is hidden'); - _gridGap?: NUMt = new NumInfo('gap between items in masonry view'); - _xMargin?: NUMt = new NumInfo('gap between left edge of document and start of masonry/stacking layouts'); - _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts'); - _xPadding?: NUMt = new NumInfo('x padding'); - _yPadding?: NUMt = new NumInfo('y padding'); + _gridGap?: NUMt = new NumInfo('gap between items in masonry view', false); + _xMargin?: NUMt = new NumInfo('gap between left edge of document and start of masonry/stacking layouts', false); + _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts', false); + _xPadding?: NUMt = new NumInfo('x padding', false); + _yPadding?: NUMt = new NumInfo('y padding', false); _singleLine?: boolean; // whether label box is restricted to one line of text _createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents - _columnWidth?: NUMt = new NumInfo('width of table column'); + _columnWidth?: NUMt = new NumInfo('width of table column', false); _columnsHideIfEmpty?: BOOLt = new BoolInfo('whether stacking view column headings should be hidden'); - _caption_xMargin?: NUMt = new NumInfo('x margin of caption inside of a carousel collection', true); - _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', true); - icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', true); - icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', true); + _caption_xMargin?: NUMt = new NumInfo('x margin of caption inside of a carousel collection', false, true); + _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', false, true); + icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', false, true); + icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', false, true); _text_fontSize?: string; _text_fontFamily?: string; _text_fontWeight?: string; @@ -250,11 +261,11 @@ export class DocumentOptions { infoWindowOpen?: BOOLt = new BoolInfo('whether info window corresponding to pin is open (on MapDocuments)'); _carousel_index?: NUMt = new NumInfo('which item index the carousel viewer is showing'); - _label_minFontSize?: NUMt = new NumInfo('minimum font size for labelBoxes'); - _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes'); - stroke_width?: NUMt = new NumInfo('width of an ink stroke'); - icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)'); - mediaState?: STRt = new StrInfo('status of audio/video media document: "pendingRecording", "recording", "paused", "playing"'); + _label_minFontSize?: NUMt = new NumInfo('minimum font size for labelBoxes', false); + _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false); + stroke_width?: NUMt = new NumInfo('width of an ink stroke', false); + icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)', false); + mediaState?: STRt = new StrInfo('status of audio/video media document: "pendingRecording", "recording", "paused", "playing"', false); recording?: BOOLt = new BoolInfo('whether WebCam is recording or not'); autoPlayAnchors?: BOOLt = new BoolInfo('whether to play audio/video when an anchor is clicked in a stackedTimeline.'); dontPlayLinkOnSelect?: BOOLt = new BoolInfo('whether an audio/video should start playing when a link is followed to it.'); @@ -270,7 +281,7 @@ export class DocumentOptions { contextMenuIcons?: List; childFilters_boolean?: string; childFilters?: List; - childLimitHeight?: NUMt = new NumInfo('whether to limit the height of collection children. 0 - means height can be no bigger than width'); + childLimitHeight?: NUMt = new NumInfo('whether to limit the height of collection children. 0 - means height can be no bigger than width', false); childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view) childLayoutString?: string; // template string for collection to use to render its children childDocumentsActive?: BOOLt = new BoolInfo('whether child documents are active when parent is document active'); @@ -282,12 +293,12 @@ export class DocumentOptions { childContextMenuIcons?: List; targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc) - lastFrame?: NUMt = new NumInfo('the last frame of a frame-based collection (e.g., progressive slide)'); - activeFrame?: NUMt = new NumInfo('the active frame of a document in a frame base collection'); - appearFrame?: NUMt = new NumInfo('the frame in which the document appears'); - _currentFrame?: NUMt = new NumInfo('the current frame of a frame-based collection (e.g., progressive slide)'); + lastFrame?: NUMt = new NumInfo('the last frame of a frame-based collection (e.g., progressive slide)', false); + activeFrame?: NUMt = new NumInfo('the active frame of a document in a frame base collection', false); + appearFrame?: NUMt = new NumInfo('the frame in which the document appears', false); + _currentFrame?: NUMt = new NumInfo('the current frame of a frame-based collection (e.g., progressive slide)', false); - isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', true); + isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', false); isBaseProto?: BOOLt = new BoolInfo('is doc a base level prototype for data documents as opposed to data documents which are prototypes for layout documents. base protos are not cloned during a deep'); isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: BOOLt = new BoolInfo('is the document a template for creating other documents'); @@ -296,21 +307,23 @@ export class DocumentOptions { _isTimelineLabel?: BOOLt = new BoolInfo('is document a timeline label'); _isLightbox?: BOOLt = new BoolInfo('whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target'); - presPanX?: NUMt = new NumInfo('panX saved as a view spec'); - presPanY?: NUMt = new NumInfo('panY saved as a view spec'); - presViewScale?: NUMt = new NumInfo('viewScale saved as a view Spec'); - presTransition?: NUMt = new NumInfo('the time taken for the transition TO a document'); - presDuration?: NUMt = new NumInfo('the duration of the slide in presentation view'); + presPanX?: NUMt = new NumInfo('panX saved as a view spec', false); + presPanY?: NUMt = new NumInfo('panY saved as a view spec', false); + presViewScale?: NUMt = new NumInfo('viewScale saved as a view Spec', false); + presTransition?: NUMt = new NumInfo('the time taken for the transition TO a document', false); + presDuration?: NUMt = new NumInfo('the duration of the slide in presentation view', false); presZoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out'); data?: any; data_useCors?: BOOLt = new BoolInfo('whether CORS protocol should be used for web page'); columnHeaders?: List; // headers for stacking views schemaHeaders?: List; // headers for schema view - dockingConfig?: STRt = new StrInfo('configuration of golden layout windows (applies only if doc is rendered as a CollectionDockingView)'); + dockingConfig?: STRt = new StrInfo('configuration of golden layout windows (applies only if doc is rendered as a CollectionDockingView)', false); icon?: string; // icon used by fonticonbox to render button noteType?: string; + // STOPPING HERE + // freeform properties _freeform_backgroundGrid?: boolean; _freeform_scale?: NUMt = new NumInfo('how much a freeform view has been scaled (zoomed)'); @@ -380,7 +393,7 @@ 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_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', false, 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 diff --git a/src/client/util/.ClientUtils.ts.icloud b/src/client/util/.ClientUtils.ts.icloud deleted file mode 100644 index e5e477586..000000000 Binary files a/src/client/util/.ClientUtils.ts.icloud and /dev/null differ diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 1a923a995..93f4cf818 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -15,7 +15,8 @@ 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'; -import { DocumentOptions } from '../documents/Documents'; +import { DocumentOptions, FInfo } from '../documents/Documents'; +import { string32 } from 'pdfjs-dist/types/src/shared/util'; //slight bug when you don't click on background canvas before creating filter and the you click on the canvas @@ -28,6 +29,8 @@ interface filterProps { @observer export class FilterPanel extends React.Component { + private _documentOptions: DocumentOptions = new DocumentOptions(); + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterPanel, fieldKey); } @@ -262,12 +265,21 @@ export class FilterPanel extends React.Component { render() { // console.log('this is frist one today ' + this._allFacets); this._allFacets.forEach(element => console.log(element)); - const options = this._allFacets.filter(facet => this.activeFacetHeaders.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); + // const options = Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => pair[1].filterable ).map(facet => value: facet, label: facet) //this._allFacets.filter(facet => this.activeFacetHeaders.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); // console.log('HEELLLLLL ' + DocumentOptions); - const freeformOptions: DocumentOptions = {}; + let filteredOptions: string[] = ['author', 'tags', 'text', 'acl-Guest']; + + Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => { + if (pair[1].filterable) { + filteredOptions.push(pair[0]); + console.log('THIS IS FILTERABLE ALKDJFIIEII' + filteredOptions); + } + }); + + let options = filteredOptions.map(facet => ({ value: facet, label: facet })); - console.log('wht is this ' + freeformOptions.author); + // Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => console.log('this is first piar ' + pair[0] + ' this is second piar ' + pair[1].filterable)); return (
@@ -373,13 +385,21 @@ export class FilterPanel extends React.Component { case 'range': const domain = renderInfoDomain; + const range = renderInfoRange; + + if (range) { + console.log('this is info range ' + range[0] + ' , ' + range[1]); + } + if (domain) { + console.log('this is info domain ' + domain[0] + ', ' + domain[1]); + return (
Doc.setDocRangeFilter(this.targetDoc, facetHeader, values)} values={renderInfoRange!}> -- cgit v1.2.3-70-g09d2 From 6297cd58741f39ce7f6510e0a4cc634d62d4778e Mon Sep 17 00:00:00 2001 From: monoguitari <113245090+monoguitari@users.noreply.github.com> Date: Thu, 24 Aug 2023 14:34:16 -0400 Subject: before meeting with Andy --- src/client/util/CurrentUserUtils.ts | 3 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 295 +++++++++++---------- .../views/nodes/RecordingBox/RecordingView.tsx | 1 + src/client/views/nodes/trails/PresBox.tsx | 1 + src/client/views/nodes/trails/PresElementBox.tsx | 4 +- 5 files changed, 163 insertions(+), 141 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 919c56fe2..7b11e59eb 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -715,13 +715,14 @@ export class CurrentUserUtils { { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected - { title: "Audio", icon: 'video', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}}, + { title: "Video", icon: 'video', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}}, { title: "StopRec", icon: "stop", toolTip: "Stop", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecording()`}, ignoreClick: true, scripts: { onClick: `return toggleRecording(_readOnly_)`}}, { title: "Dropdown", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList, expertMode: false, funcs: {btnList: `getWorkspaceRecordings()`}, ignoreClick: true, scripts: { script: `toggleRecPlayback(value)`}}, { title: "Play Rec",icon: "play", toolTip: "Play recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsWorkspaceRecPlaying()`}, ignoreClick: true, scripts: { onClick: `return playWorkspaceRec(getCurrentRecording())`}}, { title: "Pause Rec",icon: "pause", toolTip: "Pause recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsWorkspaceRecPaused()`}, ignoreClick: true, scripts: { onClick: `return pauseWorkspaceRec(getCurrentRecording())`}}, { title: "Stop Rec", icon: "stop", toolTip: "Stop recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecPlayback()`}, ignoreClick: true, scripts: { onClick: `return closeWorkspaceRec(getCurrentRecording())`}}, { title: "Add doc", icon: "down", toolTip: "add to doc", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecPlayback()`}, ignoreClick: true, scripts: { onClick: `addRectoWorkspace(getCurrentRecording())`}}, + { title: "Delete Rec", icon: "trash", toolTip: "delete selected recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecPlayback()`}, ignoreClick: true, scripts: { onClick: `removeWorkspaceRec(getCurrentRecording())`}} ]; } diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 429fc60c2..c73082f78 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -1,4 +1,4 @@ -import { action, observable } from 'mobx'; +import { action, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { VideoField } from '../../../../fields/URLField'; @@ -26,6 +26,7 @@ import { listSpec } from '../../../../fields/Schema'; import { DragManager } from '../../../util/DragManager'; import { SelectionManager } from '../../../util/SelectionManager'; import { AudioBox } from '../AudioBox'; +import { UndoManager, undoBatch } from '../../../util/UndoManager'; @observer export class RecordingBox extends ViewBoxBaseComponent() { @@ -66,6 +67,146 @@ export class RecordingBox extends ViewBoxBaseComponent() { } }; + /** + * This method toggles whether or not we are currently using the RecordingBox to record with the topbar button + * @param _readOnly_ + * @returns + */ + @undoBatch + @action + public static toggleWorkspaceRecording(_readOnly_: boolean) { + if (_readOnly_) return RecordingBox.screengrabber ? true : false; + if (RecordingBox.screengrabber) { + //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening + console.log('grabbing screen!') + RecordingBox.screengrabber.Pause?.(); + const remDoc = RecordingBox.screengrabber.rootDoc; + setTimeout(() => { + RecordingBox.screengrabber?.Finish?.(); + RecordingBox.screengrabber!.rootDoc.overlayX = 70; //was 100 + RecordingBox.screengrabber!.rootDoc.overlayY = 590; + console.log(RecordingBox.screengrabber) + RecordingBox.screengrabber = undefined; + }, 100); + //could break if recording takes too long to turn into videobox. If so, either increase time on setTimeout below or find diff place to do this + setTimeout(() => { + Doc.RemFromMyOverlay(remDoc); + + }, 1000) + Doc.UserDoc().isRecording = false; + Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc); + } else { + //when we first press mic + const screengrabber = Docs.Create.WebCamDocument('', { + _width: 205, + _height: 115, + }); + screengrabber.overlayX = 70; //was -400 + screengrabber.overlayY = 590; //was 0 + screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; + Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay + DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { + RecordingBox.screengrabber = docView.ComponentView as RecordingBox; + RecordingBox.screengrabber.Record?.(); + }); + Doc.UserDoc().isRecording = true; + } + } + + /** + * This method changes the menu depending on whether or not we are in playback mode + * @param value RecordingBox rootdoc + */ + @undoBatch + @action + public static toggleWorkspaceRecPlayback(value: Doc) { + let docval = undefined; + Doc.UserDoc().currentRecording = value; + console.log(value) + value.overlayX =70; + value.overlayY = 590; + if (!Doc.UserDoc().isAddRecToDocMode) { + Doc.UserDoc().isRecPlayback = true; + Doc.UserDoc().isWorkspaceRecPlaying = true; + Doc.AddToMyOverlay(value); + DocumentManager.Instance.AddViewRenderedCb(value, docView => { + Doc.UserDoc().currentRecording = docView.ComponentView as RecordingBox; + SelectionManager.SelectSchemaViewDoc(value); + })} else { + let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value); + DragManager.StartDropdownDrag([document.createElement('div')],new DragManager.DocumentDragData([value]), 1, 1, recordingIndex); + } + } + + /** + * Adds the recording box to the canvas + * @param value current recordingbox + */ + @undoBatch + @action + public static addRecToWorkspace(value: RecordingBox) { + console.log("adding rec to doc"); + console.log(value); + let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); + (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value.rootDoc); + let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc); + console.log(recordingIndex); + Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1); + Doc.UserDoc().isAddRecToDocMode = false; + Doc.RemFromMyOverlay(value.rootDoc); + Doc.UserDoc().currentRecording = undefined; + Doc.UserDoc().isRecPlayback = false; + Doc.UserDoc().isAddRecToDocMode = false; + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = false; + } + + @undoBatch + @action + public static playWorkspaceRec(value: VideoBox) { + value.Play(); + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = true; + } + + @undoBatch + @action + public static pauseWorkspaceRec(value: VideoBox) { + value.Pause(); + Doc.UserDoc().isWorkspaceRecPlaying = true; + Doc.UserDoc().isWorkspaceRecPaused = false; + } + + @undoBatch + @action + public static closeWorkspaceRec(value: VideoBox) { + value.Pause(); + Doc.RemFromMyOverlay(value.rootDoc); + Doc.UserDoc().currentRecording = undefined; + Doc.UserDoc().isRecPlayback = false; + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = false; + Doc.RemFromMyOverlay(value.rootDoc); + + } + + @undoBatch + @action + public static removeWorkspaceRec(value: VideoBox) { + let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc); + Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1); + Doc.UserDoc().isAddRecToDocMode = false; + Doc.RemFromMyOverlay(value.rootDoc); + Doc.UserDoc().currentRecording = undefined; + Doc.UserDoc().isRecPlayback = false; + Doc.UserDoc().isAddRecToDocMode = false; + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = false; + + } + + + Record: undefined | (() => void); Pause: undefined | (() => void); Finish: undefined | (() => void); @@ -92,143 +233,21 @@ export class RecordingBox extends ViewBoxBaseComponent() { } static screengrabber: RecordingBox | undefined; } -ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { - console.log(_readOnly_) - console.log(RecordingBox.screengrabber) - if (_readOnly_) return RecordingBox.screengrabber ? true : false; - if (RecordingBox.screengrabber) { - //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening - console.log('grabbing screen!') - RecordingBox.screengrabber.Pause?.(); - const remDoc = RecordingBox.screengrabber.rootDoc; - setTimeout(() => { - RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 70; //was 100 - RecordingBox.screengrabber!.rootDoc.overlayY = 610; - // DocListCast(Doc.MyOverlayDocs.data) - // .filter(doc => doc.slides === RecordingBox.screengrabber!.rootDoc) - // .forEach(Doc.RemFromMyOverlay); - console.log(RecordingBox.screengrabber) - RecordingBox.screengrabber = undefined; - }, 100); - //could break if recording takes too long to turn into videobox. If so, either increase time on setTimeout below or find diff place to do this - setTimeout(() => { - Doc.RemFromMyOverlay(remDoc); - - }, 1000) - Doc.UserDoc().isRecording = false; - - // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc); - Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc); - - // let recView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); - // console.log(recView) - } else { - //when we first press mic - const screengrabber = Docs.Create.WebCamDocument('', { - _width: 205, - _height: 115, - }); - // Doc.UserDoc().currentScreenGrab = screengrabber; - screengrabber.overlayX = 70; //was -400 - screengrabber.overlayY = 610; //was 0 - screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; - Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay - DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { - RecordingBox.screengrabber = docView.ComponentView as RecordingBox; - RecordingBox.screengrabber.Record?.(); - }); - // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber); - Doc.UserDoc().isRecording = true; - - } -}, 'toggle recording'); -ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { - let docval = undefined; - Doc.UserDoc().currentRecording = value; - console.log(value) - value.overlayX =70; - value.overlayY = 610; - if (!Doc.UserDoc().isAddRecToDocMode) { - Doc.UserDoc().isRecPlayback = true; - Doc.UserDoc().isWorkspaceRecPlaying = true; - Doc.AddToMyOverlay(value); - DocumentManager.Instance.AddViewRenderedCb(value, docView => { - // docval = - Doc.UserDoc().currentRecording = docView.ComponentView as RecordingBox; - SelectionManager.SelectSchemaViewDoc(value); - - // docval.Play(); - - })} else { - let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value); - DragManager.StartDropdownDrag([document.createElement('div')],new DragManager.DocumentDragData([value]), 1, 1, recordingIndex); - } - - // let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); - // (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); -}); -ScriptingGlobals.add(function addRectoWorkspace(value: RecordingBox) { - console.log("adding rec to doc"); - console.log(value); - let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); - (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value.rootDoc); - let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc); - console.log(recordingIndex); - Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1); - Doc.UserDoc().isAddRecToDocMode = false; - Doc.RemFromMyOverlay(value.rootDoc); - Doc.UserDoc().currentRecording = undefined; - Doc.UserDoc().isRecPlayback = false; - Doc.UserDoc().isAddRecToDocMode = false; - // Doc.UserDoc().isAddRecToDocMode = true; - Doc.UserDoc().isWorkspaceRecPlaying = false; - Doc.UserDoc().isWorkspaceRecPaused = false; - // let audiodoc: Doc = Docs.Create.AudioDocument(value.dataDoc.data, { - // x: 100, - // y: 100 - // }); - - // (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(audiodoc); - -}) -ScriptingGlobals.add(function playWorkspaceRec(value: VideoBox) { - value.Play(); - Doc.UserDoc().isWorkspaceRecPlaying = false; - Doc.UserDoc().isWorkspaceRecPaused = true; -}) -ScriptingGlobals.add(function pauseWorkspaceRec(value: VideoBox) { - value.Pause(); - Doc.UserDoc().isWorkspaceRecPlaying = true; - Doc.UserDoc().isWorkspaceRecPaused = false; -}) -ScriptingGlobals.add(function closeWorkspaceRec(value: VideoBox) { - value.Pause(); - Doc.RemFromMyOverlay(value.rootDoc); - Doc.UserDoc().currentRecording = undefined; - Doc.UserDoc().isRecPlayback = false; - Doc.UserDoc().isWorkspaceRecPlaying = false; - Doc.UserDoc().isWorkspaceRecPaused = false; -}) - -ScriptingGlobals.add(function getWorkspaceRecordings() { - return Doc.UserDoc().workspaceRecordings -}); -ScriptingGlobals.add(function getIsRecording() { - return Doc.UserDoc().isRecording; -}) -ScriptingGlobals.add(function getIsRecPlayback() { - return Doc.UserDoc().isRecPlayback; -}) -ScriptingGlobals.add(function getCurrentRecording() { - return Doc.UserDoc().currentRecording; -}) -ScriptingGlobals.add(function getIsWorkspaceRecPlaying() { - return Doc.UserDoc().isWorkspaceRecPlaying; -}) -ScriptingGlobals.add(function getIsWorkspaceRecPaused() { - return Doc.UserDoc().isWorkspaceRecPaused; -}) +ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { RecordingBox.toggleWorkspaceRecording(_readOnly_); }); +ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { RecordingBox.toggleWorkspaceRecPlayback(value); }); +ScriptingGlobals.add(function addRectoWorkspace(value: RecordingBox) { RecordingBox.addRecToWorkspace(value); }); + +ScriptingGlobals.add(function playWorkspaceRec(value: VideoBox) { RecordingBox.playWorkspaceRec(value); }); +ScriptingGlobals.add(function pauseWorkspaceRec(value: VideoBox) { RecordingBox.pauseWorkspaceRec(value); }); +ScriptingGlobals.add(function closeWorkspaceRec(value: VideoBox) { RecordingBox.closeWorkspaceRec(value); }); +ScriptingGlobals.add(function removeWorkspaceRec(value: VideoBox) { RecordingBox.removeWorkspaceRec(value) }); + +ScriptingGlobals.add(function getWorkspaceRecordings() { return Doc.UserDoc().workspaceRecordings }); +ScriptingGlobals.add(function getIsRecording() { return Doc.UserDoc().isRecording; }) +ScriptingGlobals.add(function getIsRecPlayback() { return Doc.UserDoc().isRecPlayback; }) +ScriptingGlobals.add(function getCurrentRecording() { return Doc.UserDoc().currentRecording; }) +ScriptingGlobals.add(function getIsWorkspaceRecPlaying() { return Doc.UserDoc().isWorkspaceRecPlaying; }) +ScriptingGlobals.add(function getIsWorkspaceRecPaused() { return Doc.UserDoc().isWorkspaceRecPaused; }) diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 8c0f5efef..755f1adc0 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -9,6 +9,7 @@ import { Networking } from '../../../Network'; import { Presentation, TrackMovements } from '../../../util/TrackMovements'; import { ProgressBar } from './ProgressBar'; import './RecordingView.scss'; +import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; export interface MediaSegment { videoChunks: any[]; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index b9585b132..dd91660ec 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -298,6 +298,7 @@ export class PresBox extends ViewBoxBaseComponent() { // Called when the user activates 'next' - to move to the next part of the pres. trail @action next = () => { + console.log("next"); const progressiveReveal = (first: boolean) => { const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null); if (presIndexed !== undefined) { diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index ee4498e53..d90f96249 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -481,9 +481,9 @@ export class PresElementBox extends ViewBoxBaseComponent() { style={{fontWeight: 700, display: 'flex'}} > , tooltip: "Save data to presentation", val: 'floppy', + {icon: , tooltip: "Save data to presentation", val: 'revert', onPointerDown: e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))}, - {icon: , tooltip: "Continously update content", val: "revert", + {icon: , tooltip: "Continously update content", val: "floppy-disk", onPointerDown: e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.revertToPreviouslySaved(targetDoc, activeItem))}, ]} />
-- cgit v1.2.3-70-g09d2 From 87f2cb52f59f3441ad3d46adc135492ae9924b89 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Thu, 24 Aug 2023 14:59:36 -0400 Subject: context menu updates --- src/client/documents/Documents.ts | 2 +- src/fields/Doc.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f5fa38ec6..85119a0be 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1567,7 +1567,7 @@ export namespace DocUtils { const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) .filter(btnDoc => !btnDoc.hidden) .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) - .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyDataViz) + .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title && doc.title != '') .map((dragDoc, i) => ({ description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), event: undoable((args: { x: number; y: number }) => { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index ceacb8a08..92d1aef83 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1577,12 +1577,13 @@ export namespace Doc { // prettier-ignore export function toIcon(doc?: Doc, isOpen?: boolean) { + console.log(doc!.title, doc!.type) switch (isOpen !== undefined ? DocumentType.COL: StrCast(doc?.type)) { case DocumentType.IMG: return 'image'; case DocumentType.COMPARISON: return 'columns'; case DocumentType.RTF: return 'sticky-note'; case DocumentType.COL: - const folder: IconProp = isOpen === true ? 'folder-open' : isOpen === false ? 'folder' : 'question'; + const folder: IconProp = isOpen === true ? 'folder-open' : isOpen === false ? 'folder' : doc!.title=='Untitled Collection'? 'object-group': 'chalkboard'; const chevron: IconProp = isOpen === true ? 'chevron-down' : isOpen === false ? 'chevron-right' : 'question'; return !doc?.isFolder ? folder : chevron; case DocumentType.WEB: return 'globe-asia'; @@ -1598,6 +1599,10 @@ export namespace Doc { case DocumentType.PDF: return 'file-pdf'; case DocumentType.LINK: return 'link'; case DocumentType.MAP: return 'map-marker-alt'; + case DocumentType.DATAVIZ: return 'chart-bar'; + case DocumentType.EQUATION: return 'calculator'; + case DocumentType.SIMULATION: return 'rocket'; + case DocumentType.CONFIG: return 'question-circle'; default: return 'question'; } } -- cgit v1.2.3-70-g09d2 From d8b2b68e64f640d3bd84efa9d4095ab97991f315 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Thu, 24 Aug 2023 17:52:03 -0400 Subject: colors --- src/client/views/MainView.tsx | 5 +++-- .../views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e376c4fdf..dd7a40cbd 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -805,8 +805,9 @@ export class MainView extends React.Component { {this.dockingContent} {this._hideUI ? null : ( -
- +
+
)}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index 0f51fe6ff..71900c63f 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -61,7 +61,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu { } color={this.userColor} /> -- cgit v1.2.3-70-g09d2 From e09aaab486733de052f362d5710794f0b0e17d62 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Thu, 24 Aug 2023 18:05:09 -0400 Subject: PropertiesView documentation info icon cleanup --- src/client/views/PropertiesView.scss | 2 ++ src/client/views/PropertiesView.tsx | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index a0b054851..e3a481206 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -23,6 +23,8 @@ padding: 10px; font-size: 24px; font-weight: bold; + display: flex; + flex-direction: row; } overflow-x: hidden; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 82ab54787..9e054b876 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1663,9 +1663,9 @@ 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/')}> + } color={StrCast(Doc.UserDoc().userColor)} /> +
-- cgit v1.2.3-70-g09d2 From 68ddebab45946697270c5f291ff9fdd044b6e83d Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Fri, 25 Aug 2023 17:25:22 +0200 Subject: filter - finishing up --- src/client/views/FilterPanel.scss | 8 ++++---- src/client/views/FilterPanel.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss index 421bce6d6..d6d2956aa 100644 --- a/src/client/views/FilterPanel.scss +++ b/src/client/views/FilterPanel.scss @@ -223,10 +223,10 @@ font-weight: bold; } -// .sliderBox-outerDiv { -// display: flex; -// align-items: center; -// } +.sliderBox-outerDiv { + display: inline-block; + vertical-align: middle; +} // .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 diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index f1eeb6fa7..11c2fc86c 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -392,9 +392,11 @@ export class FilterPanel extends React.Component { return ( <> -
- {/* console.log('on change'))} /> */} + {/*
+ console.log('on change'))} /> +
*/} +
Date: Fri, 25 Aug 2023 22:30:00 -0400 Subject: merge --- src/client/documents/Documents.ts | 2 +- .../CollectionFreeFormLinksView.tsx | 5 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 171 ++++++------- .../nodes/DataVizBox/components/Histogram.tsx | 193 +++++++-------- .../nodes/DataVizBox/components/LineChart.tsx | 100 +++++--- .../views/nodes/DataVizBox/components/PieChart.tsx | 137 +++++------ .../views/nodes/DataVizBox/components/TableBox.tsx | 270 +++++++++++---------- 7 files changed, 449 insertions(+), 429 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 85119a0be..54b6bac79 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -680,7 +680,7 @@ export namespace Docs { DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { _layout_fitWidth: true, nativeDimModifiable: true }, + options: { dataViz_title: '', dataViz: 'table', _layout_fitWidth: true, nativeDimModifiable: true }, }, ], [ diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 7c869af24..420e6a318 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -12,10 +12,7 @@ export class CollectionFreeFormLinksView extends React.Component { @computed get uniqueConnections() { return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)) .filter(c => !LightboxView.LightboxDoc || (LightboxView.IsLightboxDocView(c.a.docViewPath) && LightboxView.IsLightboxDocView(c.b.docViewPath))) - .map(c => { - console.log("got a connectoin", c) - return ; - }); + .map(c => ); } render() { diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index f936971a6..b98f65054 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -1,3 +1,4 @@ +import { Toggle, ToggleType, Type } from 'browndash-components'; import { action, computed, ObservableMap } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -9,12 +10,11 @@ import { Docs } from '../../../documents/Documents'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps } from '../trails'; +import { Histogram } from './components/Histogram'; import { LineChart } from './components/LineChart'; +import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; import './DataVizBox.scss'; -import { Histogram } from './components/Histogram'; -import { PieChart } from './components/PieChart'; -import { Toggle, ToggleType, Type } from 'browndash-components'; export enum DataVizView { TABLE = 'table', @@ -29,19 +29,29 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { return FieldView.LayoutString(DataVizBox, fieldStr); } - // all data - static pairSet = new ObservableMap(); - @computed.struct get pairs() { - return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); - } + // all datasets that have been retrieved from the server stored as a map from the dataset url to an array of records + static dataset = new ObservableMap(); + private _vizRenderer: LineChart | Histogram | PieChart | undefined; - private _chartRenderer: LineChart | Histogram | PieChart | undefined; + // all CSV records in the dataset + @computed.struct get records() { + return DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); + } - // current displayed chart type + // currently chosen visualization type: line, pie, histogram, table @computed get dataVizView(): DataVizView { return StrCast(this.layoutDoc._dataViz, 'table') as DataVizView; } + @computed get dataUrl() { + return Cast(this.dataDoc[this.fieldKey], CsvField); + } + @computed.struct get axes() { + return StrListCast(this.layoutDoc.dataViz_axes); + } + + selectAxes = (axes: string[]) => (this.layoutDoc.dataViz_axes = new List(axes)); + @action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors restoreView = (data: Doc) => { const changedView = this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz); @@ -55,7 +65,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { this.layoutDoc['_' + key] = data[key]; } }); - const func = () => this._chartRenderer?.restoreView(data); + const func = () => this._vizRenderer?.restoreView(data); if (changedView || changedAxes) { setTimeout(func, 100); return true; @@ -65,7 +75,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => { const anchor = !pinProps ? this.rootDoc - : this._chartRenderer?.getAnchor(pinProps) ?? + : this._vizRenderer?.getAnchor(pinProps) ?? Docs.Create.ConfigDocument({ // when we clear selection -> we should have it so chartBox getAnchor returns undefined // this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker) @@ -86,87 +96,78 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { return anchor; }; - @computed.struct get axes() { - return StrListCast(this.layoutDoc.dataViz_axes); + componentDidMount() { + this.props.setContentView?.(this); + if (!DataVizBox.dataset.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) this.fetchData(); } - selectAxes = (axes: string[]) => (this.layoutDoc.dataViz_axes = new List(axes)); + + fetchData = () => { + DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests + fetch('/csvData?uri=' + this.dataUrl?.url.href) // + .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res)))); + }; // toggles for user to decide which chart type to view the data in - @computed get selectView() { + renderVizView = () => { const width = this.props.PanelWidth() * 0.9; const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; const margin = { top: 10, right: 25, bottom: 75, left: 45 }; - if (!this.pairs) return 'no data'; - switch (this.dataVizView) { - case DataVizView.TABLE: - return ; - case DataVizView.LINECHART: - return ( - (this._chartRenderer = r ?? undefined)} - height={height} - width={width} - fieldKey={this.fieldKey} - margin={margin} - rootDoc={this.rootDoc} - axes={this.axes} - pairs={this.pairs} - dataDoc={this.dataDoc} - /> - ); - case DataVizView.HISTOGRAM: - return ( - (this._chartRenderer = r ?? undefined)} - height={height} - width={width} - fieldKey={this.fieldKey} - margin={margin} - rootDoc={this.rootDoc} - axes={this.axes} - pairs={this.pairs} - dataDoc={this.dataDoc} - /> - ); - case DataVizView.PIECHART: - return ( - (this._chartRenderer = r ?? undefined)} - height={height} - width={width} - fieldKey={this.fieldKey} - margin={margin} - rootDoc={this.rootDoc} - axes={this.axes} - pairs={this.pairs} - dataDoc={this.dataDoc} - /> - ); + if (this.records) { + switch (this.dataVizView) { + case DataVizView.TABLE: + return ; + case DataVizView.LINECHART: + return ( + (this._vizRenderer = r ?? undefined)} + height={height} + width={width} + fieldKey={this.fieldKey} + margin={margin} + rootDoc={this.rootDoc} + axes={this.axes} + records={this.records} + dataDoc={this.dataDoc} + /> + ); + case DataVizView.HISTOGRAM: + return ( + (this._vizRenderer = r ?? undefined)} + height={height} + width={width} + fieldKey={this.fieldKey} + margin={margin} + rootDoc={this.rootDoc} + axes={this.axes} + records={this.records} + dataDoc={this.dataDoc} + /> + ); + case DataVizView.PIECHART: + return ( + (this._vizRenderer = r ?? undefined)} + height={height} + width={width} + fieldKey={this.fieldKey} + margin={margin} + rootDoc={this.rootDoc} + axes={this.axes} + records={this.records} + dataDoc={this.dataDoc} + /> + ); + } } - } - - @computed get dataUrl() { - return Cast(this.dataDoc[this.fieldKey], CsvField); - } - - componentDidMount() { - this.props.setContentView?.(this); - this.fetchData(); - if (!this.layoutDoc._dataViz) this.layoutDoc._dataViz = this.dataVizView; - } - - fetchData() { - if (DataVizBox.pairSet.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) return; - DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []); - fetch('/csvData?uri=' + this.dataUrl?.url.href) // - .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res)))); - } + return 'no data/visualization'; + }; render() { - return !this.pairs?.length ? ( + return !this.records?.length ? ( // displays how to get data into the DataVizBox if its empty
To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + p' to bring the data table to your canvas.
) : ( @@ -189,7 +190,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz == DataVizView.HISTOGRAM} /> (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.PIECHART} />
- {this.selectView} + {this.renderVizView()}
); } diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 1eb57a323..edb1665f2 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -1,26 +1,26 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components'; +import * as d3 from 'd3'; +import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, StrListCast } from '../../../../../fields/Doc'; import * as React from 'react'; -import * as d3 from 'd3'; -import { IReactionDisposer, action, computed, observable, reaction } from 'mobx'; -import { LinkManager } from '../../../../util/LinkManager'; -import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; -import { PinProps, PresBox } from '../../trails'; -import { Docs } from '../../../../documents/Documents'; -import { List } from '../../../../../fields/List'; -import './Chart.scss'; -import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components'; import { FaFillDrip } from 'react-icons/fa'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Doc, NumListCast, StrListCast } from '../../../../../fields/Doc'; +import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; +import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; +import { Docs } from '../../../../documents/Documents'; +import { LinkManager } from '../../../../util/LinkManager'; +import { undoable } from '../../../../util/UndoManager'; +import { PinProps, PresBox } from '../../trails'; import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils'; -import { undoBatch, undoable } from '../../../../util/UndoManager'; +import './Chart.scss'; export interface HistogramProps { rootDoc: Doc; layoutDoc: Doc; axes: string[]; - pairs: { [key: string]: any }[]; + records: { [key: string]: any }[]; width: number; height: number; dataDoc: Doc; @@ -46,44 +46,47 @@ export class Histogram extends React.Component { private selectedData: any = undefined; // Selection of selected bar private hoverOverData: any = undefined; // Selection of bar being hovered over + @computed get _tableDataIds() { + return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows); + } + // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent) + @computed get _tableData() { + return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]); + } // filters all data to just display selected data if brushed (created from an incoming link) @computed get _histogramData() { - var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids); if (this.props.axes.length < 1) return []; if (this.props.axes.length < 2) { var ax0 = this.props.axes[0]; - if (/\d/.test(this.props.pairs[0][ax0])) { + if (/\d/.test(this.props.records[0][ax0])) { this.numericalXData = true; } - return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]))) - .map(pair => ({ [ax0]: pair[this.props.axes[0]] })); + return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] })); } var ax0 = this.props.axes[0]; var ax1 = this.props.axes[1]; - if (/\d/.test(this.props.pairs[0][ax0])) { + if (/\d/.test(this.props.records[0][ax0])) { this.numericalXData = true; } - if (/\d/.test(this.props.pairs[0][ax1])) { + if (/\d/.test(this.props.records[0][ax1])) { this.numericalYData = true; } - return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]))) - .map(pair => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] })); + return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] })); } @computed get defaultGraphTitle() { var ax0 = this.props.axes[0]; var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined; - if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.pairs[0][ax1]) || !this.numericalYData) { + if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.records[0][ax1]) || !this.numericalYData) { return ax0 + ' Histogram'; } else return ax0 + ' by ' + ax1 + ' Histogram'; } - @computed get incomingLinks() { - return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - .filter(link => link.link_anchor_1 == this.props.rootDoc.draggedFrom) // get links where this chart doc is the target of the link - .map(link => DocCast(link.link_anchor_1)); // then return the source of the link + @computed get parentViz() { + return DocCast(this.props.rootDoc.dataViz_parentViz); + // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links + // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link + // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } { @@ -100,11 +103,7 @@ export class Histogram extends React.Component { componentDidMount = () => { this._disposers.chartData = reaction( () => ({ dataSet: this._histogramData, w: this.width, h: this.height }), - ({ dataSet, w, h }) => { - if (dataSet!.length > 0) { - this.drawChart(dataSet, w, h); - } - }, + ({ dataSet, w, h }) => dataSet!.length > 0 && this.drawChart(dataSet, w, h), { fireImmediately: true } ); if (!this.props.layoutDoc.dataViz_histogram) this.props.layoutDoc.dataViz_histogram = ''; @@ -115,7 +114,6 @@ export class Histogram extends React.Component { // create a document anchor that stores whatever is needed to reconstruct the viewing state (selection,zoom,etc) getAnchor = (pinProps?: PinProps) => { const anchor = Docs.Create.ConfigDocument({ - // title: 'histogram doc selection' + this._currSelected, }); PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc); @@ -132,21 +130,15 @@ export class Histogram extends React.Component { // cleans data by converting numerical data to numbers and taking out empty cells data = (dataSet: any) => { - var validData = dataSet.filter((d: { [x: string]: unknown }) => { - var valid = true; - Object.keys(dataSet[0]).map(key => { - if (!d[key] || Number.isNaN(d[key])) valid = false; - }); - return valid; - }); - var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined; - const data = validData.map((d: { [x: string]: any }) => { - if (this.numericalXData) { - return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\ !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key]))); + const field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined; + return !field + ? [] + : validData.map((d: { [x: string]: any }) => + !this.numericalXData // + ? d[field] + : +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { d3.select(this._histogramRef.current).select('svg').remove(); d3.select(this._histogramRef.current).select('.tooltip').remove(); - var data = this.data(dataSet); - var xAxisTitle = Object.keys(dataSet[0])[0]; - var yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency'; - let uniqueArr: unknown[] = [...new Set(data)]; + const data = this.data(dataSet); + const xAxisTitle = Object.keys(dataSet[0])[0]; + const yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency'; + const uniqueArr: unknown[] = [...new Set(data)]; var numBins = this.numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length; var translateXAxis = !this.numericalXData || numBins < this.maxBins ? width / (numBins + 1) / 2 : 0; if (numBins > this.maxBins) numBins = this.maxBins; - var startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0; - var endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins; + const startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0; + const endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins; // converts data into Objects - var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => { - var valid = true; - Object.keys(dataSet[0]).map(key => { - if (!d[key] || Number.isNaN(d[key])) valid = false; - }); - return valid; - }); + var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key]))); if (!this.numericalXData) { var histStringDataSet: { [x: string]: unknown }[] = []; if (this.numericalYData) { @@ -322,7 +308,7 @@ export class Histogram extends React.Component { // click/hover const onPointClick = action((e: any) => this.highlightSelectedBar(true, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet)); const onHover = action((e: any) => { - const selected = this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet); + this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet); updateHighlights(); }); const mouseOut = action((e: any) => { @@ -361,10 +347,10 @@ export class Histogram extends React.Component { 'transform', this.numericalYData ? function (d) { - var eachData = histDataSet.filter((data: { [x: string]: number }) => { + const eachData = histDataSet.filter((data: { [x: string]: number }) => { return data[xAxisTitle] == d[0]; }); - var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { 'height', this.numericalYData ? function (d) { - var eachData = histDataSet.filter((data: { [x: string]: number }) => { + const eachData = histDataSet.filter((data: { [x: string]: number }) => { return data[xAxisTitle] == d[0]; }); - var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { 'class', selected ? function (d) { - return selected && selected[0] == d[0] ? 'histogram-bar hover' : 'histogram-bar'; + return selected && selected[0] === d[0] ? 'histogram-bar hover' : 'histogram-bar'; } : function (d) { return 'histogram-bar'; @@ -398,11 +384,11 @@ export class Histogram extends React.Component { ) .attr('fill', d => { var barColor; - var barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::')); - barColors.map(each => { + const barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::')); + barColors.forEach(each => { if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1]; else { - var range = StrCast(each[0]).split(' to '); + const range = StrCast(each[0]).split(' to '); if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1]; } }); @@ -412,26 +398,40 @@ export class Histogram extends React.Component { @action changeSelectedColor = (color: string) => { 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); - }); + barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1)); barColors.push(StrCast(barName + '::' + color)); }; @action eraseSelectedColor = () => { 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); - }); + barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1)); }; + updateBarColors = () => { + var svg = this._histogramSvg; + if (svg) svg.selectAll('rect') + .attr('fill', (d: any) => { + var barColor; + const barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::')); + barColors.forEach(each => { + if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1]; + else { + const range = StrCast(each[0]).split(' to '); + if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1]; + } + }); + return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.dataViz_histogram_defaultColor); + }); + } + render() { + this.updateBarColors(); this._histogramData; var curSelectedBarName = ''; var titleAccessor: any = ''; @@ -440,25 +440,22 @@ export class Histogram extends React.Component { if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle; 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; + var selected = 'none'; if (this._currSelected) { curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { - key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : ''; - }); - selected = selected.substring(0, selected.length - 2); - selected += ' }'; - } else selected = 'none'; + Object.keys(this._currSelected).forEach(key => + key // + ? (selected += key + ': ' + this._currSelected[key] + ', ') + : '' + ); + selected = selected.substring(0, selected.length - 2) + ' }'; + } var selectedBarColor; - var barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::')); - barColors.map(each => { - if (each[0] == curSelectedBarName!) selectedBarColor = each[1]; - }); - - this.componentDidMount(); + var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::')); + barColors.forEach(each => each[0] === curSelectedBarName && (selectedBarColor = each[1])); - if (this._histogramData.length > 0 || (!this.incomingLinks || this.incomingLinks.length==0)) { + if (this._histogramData.length > 0 || !this.parentViz) { return this.props.axes.length >= 1 ? (
@@ -515,10 +512,8 @@ export class Histogram extends React.Component { ) : ( {'first use table view to select a column to graph'} ); - } else - return ( - // when it is a brushed table and the incoming table doesn't have any rows selected -
Selected rows of data from the incoming DataVizBox to display.
- ); + } + // when it is a brushed table and the incoming table doesn't have any rows selected + return
Selected rows of data from the incoming DataVizBox to display.
; } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 2b2a5326a..206169c13 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, observable, reaction } from 'mobx' import { observer } from 'mobx-react'; import * as React from 'react'; import * as d3 from 'd3'; -import { Doc, DocListCast, StrListCast } from '../../../../../fields/Doc'; +import { Doc, DocListCast, NumListCast, StrListCast } from '../../../../../fields/Doc'; import { Id } from '../../../../../fields/FieldSymbols'; import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; @@ -28,7 +28,7 @@ export interface LineChartProps { rootDoc: Doc; layoutDoc: Doc; axes: string[]; - pairs: { [key: string]: any }[]; + records: { [key: string]: any }[]; width: number; height: number; dataDoc: Doc; @@ -49,32 +49,35 @@ export class LineChart extends React.Component { @observable _currSelected: SelectedDataPoint | undefined = undefined; // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates + @computed get _tableDataIds() { + return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows); + } + // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent) + @computed get _tableData() { + return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]); + } @computed get _lineChartData() { - var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids); + var guids = StrListCast(this.props.layoutDoc.dataViz_rowIds); if (this.props.axes.length <= 1) return []; - return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]))) - .map(pair => ({ x: Number(pair[this.props.axes[0]]), y: Number(pair[this.props.axes[1]]) })) - .sort((a, b) => (a.x < b.x ? -1 : 1)); + return this._tableData.map(record => ({ x: Number(record[this.props.axes[0]]), y: Number(record[this.props.axes[1]]) })).sort((a, b) => (a.x < b.x ? -1 : 1)); } @computed get graphTitle() { return this.props.axes[1] + ' vs. ' + this.props.axes[0] + ' Line Chart'; } - @computed get incomingLinks() { - return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - .filter(link => { - return link.link_anchor_1 == this.props.rootDoc.draggedFrom; - }) // get links where this chart doc is the target of the link - .map(link => DocCast(link.link_anchor_1)); // then return the source of the link + @computed get parentViz() { + return DocCast(this.props.rootDoc.dataViz_parentViz); + // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links + // .filter(link => { + // return link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz; + // }) // get links where this chart doc is the target of the link + // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } - @computed get incomingSelected() { + @computed get incomingHighlited() { // return selected x and y axes // otherwise, use the selection of whatever is linked to us - return this.incomingLinks // all links that are pointing to this node - .map(anchor => DocumentManager.Instance.getFirstDocumentView(anchor)?.ComponentView as DataVizBox) // get their data viz boxes - .filter(dvb => dvb) - .map(dvb => dvb.pairs?.filter(pair => pair['select' + dvb.rootDoc[Id]])) // get all the datapoints they have selected field set by incoming anchor - .lastElement(); + const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox; + const highlitedRowIds = (incomingVizBox && incomingVizBox.rootDoc)? NumListCast(incomingVizBox.rootDoc.dataViz_highlitedRows) : []; + return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor } @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } { return minMaxRange([this._lineChartData]); @@ -107,23 +110,46 @@ export class LineChart extends React.Component { this._disposers.highlights = reaction( () => ({ selected: this._currSelected, - incomingSelected: this.incomingSelected, + incomingHighlited: this.incomingHighlited, }), - ({ selected, incomingSelected }) => { + ({ selected, incomingHighlited }) => { // redraw annotations when the chart data has changed, or the local or inherited selection has changed - 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'); - } - } + this.clearAnnotations(); + selected && this.drawAnnotations(Number(selected.x), Number(selected.y), true); + incomingHighlited?.forEach((record: any) => this.drawAnnotations(Number(record[this.props.axes[0]]), Number(record[this.props.axes[1]]))); }, { 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 { + // } + } }; @action @@ -162,7 +188,7 @@ export class LineChart extends React.Component { @computed get defaultGraphTitle() { var ax0 = this.props.axes[0]; var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined; - if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) { + if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) { return ax0 + ' Line Chart'; } else return ax1 + ' by ' + ax0 + ' Line Chart'; } @@ -186,7 +212,7 @@ export class LineChart extends React.Component { // TODO: nda - get rid of svg element in the list? if (this._currSelected && this._currSelected.x == x && this._currSelected.y == y) this._currSelected = undefined; else this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined; - this.props.pairs.forEach(pair => pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y && (pair.selected = true)); + this.props.records.forEach(record => record[this.props.axes[0]] === x && record[this.props.axes[1]] === y && (record.selected = true)); } drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear, yScale: d3.ScaleLinear) { @@ -327,10 +353,10 @@ export class LineChart extends React.Component { 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)){ - return this.props.axes.length>=2 && /\d/.test(this.props.pairs[0][this.props.axes[0]]) && /\d/.test(this.props.pairs[0][this.props.axes[1]]) ? ( -
-
+ if (this._lineChartData.length > 0 || !this.parentViz || this.parentViz.length == 0) { + return this.props.axes.length >= 2 && /\d/.test(this.props.records[0][this.props.axes[0]]) && /\d/.test(this.props.records[0][this.props.axes[1]]) ? ( +
+
{ private _disposers: { [key: string]: IReactionDisposer } = {}; private _piechartRef: React.RefObject = React.createRef(); private _piechartSvg: d3.Selection | undefined; - private byCategory: boolean = true; // whether the data is organized by category or by specified number percentages/ratios - @observable _currSelected: any | undefined = undefined; // Object of selected slice private curSliceSelected: any = undefined; // d3 data of selected slice private selectedData: any = undefined; // Selection of selected slice private hoverOverData: any = undefined; // Selection of slice being hovered over + @observable _currSelected: any | undefined = undefined; // Object of selected slice + @computed get _tableDataIds() { + return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows); + } + // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent) + @computed get _tableData() { + return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]); + } + + // organized by specified number percentages/ratios if one column is selected and it contains numbers + // otherwise, assume data is organized by categories + @computed get byCategory() { + if (this.props.axes.length === 1) { + return !/\d/.test(this.props.records[0][this.props.axes[0]]); + } + return true; + } // filters all data to just display selected data if brushed (created from an incoming link) @computed get _piechartData() { - var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids); if (this.props.axes.length < 1) return []; + + const ax0 = this.props.axes[0]; if (this.props.axes.length < 2) { - var ax0 = this.props.axes[0]; - if (/\d/.test(this.props.pairs[0][ax0])) { - this.byCategory = false; - } - return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]))) - .map(pair => ({ [ax0]: pair[this.props.axes[0]] })); + return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] })); } - var ax0 = this.props.axes[0]; - var ax1 = this.props.axes[1]; - if (/\d/.test(this.props.pairs[0][ax0])) { - this.byCategory = false; - } - return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]))) - .map(pair => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] })); + const ax1 = this.props.axes[1]; + return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] })); } @computed get defaultGraphTitle() { var ax0 = this.props.axes[0]; var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined; - if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) { + if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) { return ax0 + ' Pie Chart'; - } else return ax1 + ' by ' + ax0 + ' Pie Chart'; + } + return ax1 + ' by ' + ax0 + ' Pie Chart'; } - @computed get incomingLinks() { - return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - .filter(link => link.link_anchor_1 == this.props.rootDoc.draggedFrom) // get links where this chart doc is the target of the link - .map(link => DocCast(link.link_anchor_1)); // then return the source of the link + @computed get parentViz() { + return DocCast(this.props.rootDoc.dataViz_parentViz); + // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links + // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link + // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } componentWillUnmount() { @@ -117,21 +123,15 @@ export class PieChart extends React.Component { // cleans data by converting numerical data to numbers and taking out empty cells data = (dataSet: any) => { - var validData = dataSet.filter((d: { [x: string]: unknown }) => { - var valid = true; - Object.keys(dataSet[0]).map(key => { - if (!d[key] || Number.isNaN(d[key])) valid = false; - }); - return valid; - }); - var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined; - const data = validData.map((d: { [x: string]: any }) => { - if (!this.byCategory) { - return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\ !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key]))); + const field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined; + return !field + ? undefined + : validData.map((d: { [x: string]: any }) => + this.byCategory + ? d[field] // + : +d[field].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { // converts data into Objects var data = this.data(dataSet); - var pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => { - var valid = true; - Object.keys(dataSet[0]).map(key => { - if (!d[key] || Number.isNaN(d[key])) valid = false; - }); - return valid; - }); + var pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key]))); if (this.byCategory) { let uniqueCategories = [...new Set(data)]; var pieStringDataSet: { frequency: number }[] = []; @@ -233,7 +227,7 @@ export class PieChart extends React.Component { // click/hover const onPointClick = action((e: any) => this.highlightSelectedSlice(true, svg, arc, radius, d3.pointer(e), pieDataSet)); const onHover = action((e: any) => { - const selected = this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet); + this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet); updateHighlights(); }); const mouseOut = action((e: any) => { @@ -253,16 +247,19 @@ export class PieChart extends React.Component { // drawing the slices var selected = this.selectedData; var arcs = g.selectAll('arc').data(pie(data)).enter().append('g'); + const sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::')); + const possibleDataPointVals = pieDataSet.map((each: { [x: string]: any | { valueOf(): number } }) => { + try { + each[percentField] = Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { - var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => { - try { - return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\ pval[percentField] === Number(d.data)); if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0]; else { dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]; @@ -270,11 +267,9 @@ export class PieChart extends React.Component { } var sliceColor; if (dataPoint) { - var accessByName = dataPoint[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ each.split('::')); - sliceColors.map(each => { - if (each[0] == StrCast(accessByName)) sliceColor = each[1]; - }); + sliceColors.forEach(each => each[0] == StrCast(accessByName) && (sliceColor = each[1])); } return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length]; }) @@ -317,7 +312,6 @@ export class PieChart extends React.Component { dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]; trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1; } - if (dataPoint[percentField] && dataPoint[percentField]==0) return ''; return dataPoint ? dataPoint[percentField]! + (!descriptionField ? '' : ' - ' + dataPoint[descriptionField])! : ''; }); }; @@ -334,7 +328,6 @@ export class PieChart extends React.Component { }; render() { - this.componentDidMount(); var titleAccessor: any = ''; 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]; @@ -357,7 +350,7 @@ export class PieChart extends React.Component { if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1]; }); - if (this._piechartData.length>0 || (!this.incomingLinks || this.incomingLinks.length==0)){ + if (this._piechartData.length > 0 || !this.parentViz) { return this.props.axes.length >= 1 ? (
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index d6e33d976..067dff07a 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -1,21 +1,21 @@ -import { action, computed } from 'mobx'; +import { action, computed, IReactionDisposer, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, Field, StrListCast } from '../../../../../fields/Doc'; +import { Doc, Field, NumListCast, StrListCast } from '../../../../../fields/Doc'; import { List } from '../../../../../fields/List'; +import { listSpec } from '../../../../../fields/Schema'; +import { Cast, DocCast } from '../../../../../fields/Types'; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils'; import { DragManager } from '../../../../util/DragManager'; +import { LinkManager } from '../../../../util/LinkManager'; import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; -import { LinkManager } from '../../../../util/LinkManager'; -import { Cast, DocCast } from '../../../../../fields/Types'; import './Chart.scss'; -import { listSpec } from '../../../../../fields/Schema'; interface TableBoxProps { rootDoc: Doc; layoutDoc: Doc; - pairs: { [key: string]: any }[]; + records: { [key: string]: any }[]; selectAxes: (axes: string[]) => void; axes: string[]; width: number; @@ -31,151 +31,159 @@ interface TableBoxProps { @observer export class TableBox extends React.Component { - // filters all data to just display selected data if brushed (created from an incoming link) + _inputChangedDisposer?: IReactionDisposer; + componentDidMount() { + // if the tableData changes (ie., when records are selected by the parent (input) visulization), + // then we need to remove any selected rows that are no longer part of the visualized dataset. + this._inputChangedDisposer = reaction(() => this._tableData.slice(), this.filterSelectedRowsDown, { fireImmediately: true }); + } + componentWillUnmount() { + this._inputChangedDisposer?.(); + } + @computed get _tableDataIds() { + return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows); + } + // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent) @computed get _tableData() { - if (this.incomingLinks.length! <= 0) return this.props.pairs; - var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids); - return this.props.pairs?.filter(pair => this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])); + return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]); } - @computed get incomingLinks() { - return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - .filter(link => { - return link.link_anchor_1 == this.props.rootDoc.draggedFrom; - }) // get links where this chart doc is the target of the link - .map(link => DocCast(link.link_anchor_1)); // then return the source of the link + @computed get parentViz() { + return DocCast(this.props.rootDoc.dataViz_parentViz); + // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links + // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link + // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @computed get columns() { - if (!this.props.layoutDoc.dataViz_rowGuids) this.props.layoutDoc.dataViz_rowGuids = new List(); - const guids = Cast(this.props.layoutDoc.dataViz_rowGuids, listSpec('string'), null); - if (guids.length == 0) this.props.pairs.map(row => guids.push(Utils.GenerateGuid())); return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header != '' && header != undefined) : []; } - // updates the 'selected' field to no longer include rows that aren't in the table - filterSelectedRowsDown() { - if (!this.props.layoutDoc.dataViz_selectedRows) this.props.layoutDoc.dataViz_selectedRows = new List(); - const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null); - const incomingSelected = this.incomingLinks.length ? StrListCast(this.incomingLinks[0].dataViz_selectedRows) : undefined; - if (incomingSelected) { - selected.map(guid => { - if (!incomingSelected.includes(guid)) selected.splice(selected.indexOf(guid), 1); - }); // filters through selected to remove guids that were removed in the incoming data - } - } + // updates the 'dataViz_selectedRows' and 'dataViz_highlightedRows' fields to no longer include rows that aren't in the table + filterSelectedRowsDown = () => { + const selected = NumListCast(this.props.layoutDoc.dataViz_selectedRows); + this.props.layoutDoc.dataViz_selectedRows = new List(selected.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data + const highlighted = NumListCast(this.props.layoutDoc.dataViz_highlitedRows); + this.props.layoutDoc.dataViz_highlitedRows = new List(highlighted.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data + }; render() { - this.filterSelectedRowsDown(); if (this._tableData.length > 0) { return ( -
+
+ r?.addEventListener( + 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) + (e: WheelEvent) => { + if (!r.scrollTop && e.deltaY <= 0) e.preventDefault(); + e.stopPropagation(); + }, + { passive: false } + ) + }> - {this.columns - .filter(col => !col.startsWith('select')) - .map(col => { - const header = React.createRef(); - return ( - - ); - })} + {this.columns.map(col => ( + + ))} - {this._tableData?.map((p, i) => { - var containsData = false; - var guid = StrListCast(this.props.layoutDoc.dataViz_rowGuids)![this.props.pairs.indexOf(p)]; - this.columns.map(col => { - if (p[col] != '' && p[col] != null && p[col] != undefined) containsData = true; - }); - if (containsData) { - return ( - { + {this._tableDataIds + ?.map(rowId => ({ record: this.props.records[rowId], rowId })) + .map(({ record, rowId }) => ( + { + const highlited = Cast(this.props.layoutDoc.dataViz_highlitedRows, listSpec('number'), null); + const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('number'), null); + if (e.metaKey) { + // highlighting a row + if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1); + else highlited?.push(rowId); + if (!selected?.includes(rowId)) selected?.push(rowId); + } else { // selecting a row - const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null); - if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1); - else { - selected.push(guid); - } - })} - style={{ background: StrListCast(this.props.layoutDoc.dataViz_selectedRows).includes(guid) ? 'lightgrey' : '', width: '110%' }}> - {this.columns.map(col => { - // each cell - var colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false; - return ( - - ); - })} - - ); - } - })} + if (selected?.includes(rowId)) { + if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1); + selected.splice(selected.indexOf(rowId), 1); + } else selected?.push(rowId); + } + e.stopPropagation(); + })} + style={{ + background: NumListCast(this.props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '', + width: '110%', + }}> + {this.columns.map(col => { + // each cell + const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false; + return ( + + ); + })} + + ))}
{ - const downX = e.clientX; - const downY = e.clientY; - setupMoveUpEvents( - {}, - e, - e => { - // dragging off a column to create a brushed DataVizBox - const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; - const targetCreator = (annotationOn: Doc | undefined) => { - const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); - embedding._dataViz = DataVizView.TABLE; - embedding._dataViz_axes = new List([col, col]); - embedding._draggedFrom = this.props.docView?.()!.rootDoc!; - embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; - 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())) { - DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { - dragComplete: e => { - if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { - e.linkDocument.link_displayLine = true; - e.linkDocument.link_matchEmbeddings = true; - // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; - // e.annoDragData.linkSourceDoc.followLinkZoom = false; - } - }, - }); - return true; - } - return false; - }, - emptyFunction, - action(e => { - const newAxes = this.props.axes; - if (newAxes.includes(col)) { - newAxes.splice(newAxes.indexOf(col), 1); - } else if (newAxes.length > 1) { - newAxes[1] = col; - } else { - newAxes.push(col); - } - this.props.selectAxes(newAxes); - }) - ); - }}> - {col} - { + const downX = e.clientX; + const downY = e.clientY; + setupMoveUpEvents( + {}, + e, + e => { + // dragging off a column to create a brushed DataVizBox + const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; + const targetCreator = (annotationOn: Doc | undefined) => { + const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); + embedding._dataViz = DataVizView.TABLE; + embedding._dataViz_axes = new List([col, col]); + embedding._dataViz_parentViz = this.props.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); + return embedding; + }; + if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { + DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { + dragComplete: e => { + if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { + e.linkDocument.link_displayLine = true; + e.linkDocument.link_matchEmbeddings = true; + // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; + // e.annoDragData.linkSourceDoc.followLinkZoom = false; + } + }, + }); + return true; + } + return false; + }, + emptyFunction, + action(e => { + const newAxes = this.props.axes; + if (newAxes.includes(col)) newAxes.splice(newAxes.indexOf(col), 1); + else if (newAxes.length > 1) newAxes[1] = col; + else newAxes.push(col); + this.props.selectAxes(newAxes); + }) + ); + }}> + {col} +
- {p[col]} -
+ {record[col]} +
-- cgit v1.2.3-70-g09d2 From 788fc2d0e200c7dc5b8990f38c9946db67c14d1e Mon Sep 17 00:00:00 2001 From: srichman333 Date: Fri, 25 Aug 2023 23:36:42 -0400 Subject: 2 column pie charts, click on numerical slices --- .../views/nodes/DataVizBox/components/PieChart.tsx | 28 ++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index b3e604483..8be8b7c7c 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -52,13 +52,10 @@ export class PieChart extends React.Component { // organized by specified number percentages/ratios if one column is selected and it contains numbers // otherwise, assume data is organized by categories @computed get byCategory() { - if (this.props.axes.length === 1) { - return !/\d/.test(this.props.records[0][this.props.axes[0]]); - } - return true; + return !/\d/.test(this.props.records[0][this.props.axes[0]]); } // filters all data to just display selected data if brushed (created from an incoming link) - @computed get _piechartData() { + @computed get _pieChartData() { if (this.props.axes.length < 1) return []; const ax0 = this.props.axes[0]; @@ -90,7 +87,7 @@ export class PieChart extends React.Component { } componentDidMount = () => { this._disposers.chartData = reaction( - () => ({ dataSet: this._piechartData, w: this.width, h: this.height }), + () => ({ dataSet: this._pieChartData, w: this.width, h: this.height }), ({ dataSet, w, h }) => { if (dataSet!.length > 0) { this.drawChart(dataSet, w, h); @@ -163,7 +160,7 @@ export class PieChart extends React.Component { } if (lineCrossCount % 2 != 0) { // inside the slice of it crosses an odd number of edges - var showSelected = this.byCategory ? pieDataSet[index] : this._piechartData[index]; + var showSelected = this.byCategory ? pieDataSet[index] : this._pieChartData[index]; if (changeSelectedVariables) { // for when a bar is selected - not just hovered over sameAsCurrent = this._currSelected @@ -267,9 +264,10 @@ export class PieChart extends React.Component { } var sliceColor; if (dataPoint) { - var accessByName = StrCast(dataPoint[this.props.axes[0]]).replace(/\$/g, '').replace(/\%/g, '').replace(/\ each.split('::')); - sliceColors.forEach(each => each[0] == StrCast(accessByName) && (sliceColor = each[1])); + sliceColors.forEach(each => each[0] == accessByName && (sliceColor = each[1])); } return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length]; }) @@ -318,7 +316,8 @@ export class PieChart extends React.Component { @action changeSelectedColor = (color: string) => { this.curSliceSelected.attr('fill', color); - var sliceName = this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { @@ -336,7 +335,8 @@ export class PieChart extends React.Component { var selected: string; var curSelectedSliceName = ''; if (this._currSelected) { - curSelectedSliceName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : ''; @@ -346,11 +346,9 @@ export class PieChart extends React.Component { } else selected = 'none'; var selectedSliceColor; var sliceColors = StrListCast(this.props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::')); - sliceColors.map(each => { - if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1]; - }); + sliceColors.forEach(each => { if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1] }); - if (this._piechartData.length > 0 || !this.parentViz) { + if (this._pieChartData.length > 0 || !this.parentViz) { return this.props.axes.length >= 1 ? (
-- cgit v1.2.3-70-g09d2 From 2c46608d3207a8463907b0e1904d9b3026d6d1c8 Mon Sep 17 00:00:00 2001 From: Eric Date: Sat, 26 Aug 2023 12:33:47 -0400 Subject: Final1 --- src/client/util/DocumentManager.ts | 2 +- src/client/views/SidebarAnnos.tsx | 1 + src/client/views/nodes/MapBox/MapBox.tsx | 77 ++++++++++++++++++++++++++----- src/client/views/nodes/trails/PresBox.tsx | 4 +- 4 files changed, 70 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 7c3b5be05..5b627c2f3 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -310,7 +310,7 @@ export class DocumentManager { if (viewSpec && docView) { if (docView.ComponentView instanceof FormattedTextBox) docView.ComponentView?.focus(viewSpec, options); PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500); - Doc.linkFollowHighlight(docView.rootDoc, undefined, options.effect); + Doc.linkFollowHighlight(viewSpec ? [docView.rootDoc, viewSpec]: docView.rootDoc, undefined, options.effect); if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc); if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden; if (options.effect) docView.rootDoc[Animation] = options.effect; diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 7635d719e..520485a71 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -87,6 +87,7 @@ export class SidebarAnnos extends React.Component { const taggedContent = this.childFilters() .filter(data => data.split(':')[0]) + .filter(data => data.split(':')[0] !== 'latitude' && data.split(':')[0] !== 'longitude') .map(data => { const key = data.split(':')[0]; const val = Field.Copy(this.allMetadata.get(key)); diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index dbb38e763..93020354d 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -7,7 +7,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { TbHeartMinus } from 'react-icons/tb'; import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; -import { Width } from '../../../../fields/DocSymbols'; +import { Highlight, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { ScriptField } from '../../../../fields/ScriptField'; @@ -133,6 +133,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this._disposer[key]?.()); } // iterate allMarkers to size, center, and zoom map to contain all markers @@ -353,17 +354,18 @@ export class MapBox extends ViewBoxAnnotatableComponent { + createNoteAnnotation = () => { !this.layoutDoc.layout_showSidebar && this.toggleSidebar(); - setTimeout(() =>{ - const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); - if (note && this.selectedPin) { - note.latitude = this.selectedPin.latitude; - note.longitude = this.selectedPin.latitude; - } - }); // give time for sidebarRef to be created - }, "create linked note"); + setTimeout(undoable(() => { + const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); + if (note && this.selectedPin) { + note.latitude = this.selectedPin.latitude; + note.longitude = this.selectedPin.latitude; + } + },"create note annotation")) + + } sidebarDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); }; @@ -818,7 +820,60 @@ export class MapBox extends ViewBoxAnnotatableComponent this.allMapPushpins.map(doc => doc[Highlight]), + () => this.allMapPushpins.forEach(doc => { + + // if(doc[Highlight]){ + // this.deselectPin(); + // this.selectedPin = doc; + + // Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match"); + // Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match"); + + // this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin)); + // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), { + // color: 'green', + // }); + // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc)); + // this._bingMap.current.entities.push(newpin); + // this.map_docToPinMap.set(this.selectedPin, newpin); + + // MapAnchorMenu.Instance.Delete = this.deleteSelectedPin; + // MapAnchorMenu.Instance.Center = this.centerOnSelectedPin; + // MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation; + // } + // if (doc[Highlight]) { + // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); + // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), { + // color: 'orange', + // }); + // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); + // this._bingMap.current.entities.push(newpin); + // this.map_docToPinMap.set(doc, newpin); + + // } + // if (this.map_docToPinMap.get(doc).getColor() == 'orange' && !doc[Highlight]) { + // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); + // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {}); + // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); + // this._bingMap.current.entities.push(newpin); + // this.map_docToPinMap.set(doc, newpin); + + // } + // else if (this.map_docToPinMap.get(doc).getColor() != 'orange' && doc[Highlight]) { + // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); + // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), { + // color: 'orange', + // }); + // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); + // this._bingMap.current.entities.push(newpin); + // this.map_docToPinMap.set(doc, newpin); + // } + + + }) + , {fireImmediately: true}) // this.updateMapType(); this._disposer.location = reaction( () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }), diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index a94f1f04b..afd9bccab 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -661,8 +661,8 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.config_yRange = undefined; //targetDoc?.yrange; } if (pinProps.pinData.map) { - pinDoc.config_latitude = targetDoc?.latitude; - pinDoc.config_longitude = targetDoc?.longitude; + // pinDoc.config_latitude = targetDoc?.latitude; + // pinDoc.config_longitude = targetDoc?.longitude; pinDoc.config_mapZoom = targetDoc?.mapZoom; pinDoc.config_mapType = targetDoc?.mapType; //... -- cgit v1.2.3-70-g09d2 From 534ca5e695032d598156fab6ab04ef2ecb1f3f9b Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 26 Aug 2023 16:32:55 -0400 Subject: fixed map crashing when deslecting a pin --- package-lock.json | 91 ++++++++++++++++++- src/client/views/nodes/MapBox/MapBox.tsx | 150 ++++++++++++++----------------- 2 files changed, 155 insertions(+), 86 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 356224577..00410e3be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7094,12 +7094,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" + } + } + } } } }, @@ -8604,7 +8689,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", @@ -8627,7 +8712,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" @@ -8786,7 +8871,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/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 93020354d..48bc02f84 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -357,15 +357,16 @@ export class MapBox extends ViewBoxAnnotatableComponent { !this.layoutDoc.layout_showSidebar && this.toggleSidebar(); - setTimeout(undoable(() => { - const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); - if (note && this.selectedPin) { - note.latitude = this.selectedPin.latitude; - note.longitude = this.selectedPin.latitude; - } - },"create note annotation")) - - } + setTimeout( + undoable(() => { + const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); + if (note && this.selectedPin) { + note.latitude = this.selectedPin.latitude; + note.longitude = this.selectedPin.latitude; + } + }, 'create note annotation') + ); + }; sidebarDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); }; @@ -510,7 +511,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this.selectedPin) { // Removes filter - Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "remove"); - Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.longitude, "remove"); + Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove'); + Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove'); const temp = this.selectedPin; this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp)); @@ -539,10 +540,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { @@ -557,8 +555,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { - if (this.selectedPin) { - this.deselectPin(); - this.MicrosoftMaps.Events.removeEventListener(this._bingMap.current, 'click', this.mapOnClick); - } - }; - + mapOnClick = (e: { location: { latitude: any; longitude: any } }) => this.deselectPin(); /* * Updates values of layout doc to match the current map */ @@ -720,15 +709,14 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this.selectedPin) { // Removes filter - Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "remove"); - Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.longitude, "remove"); + Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove'); + Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove'); this.removePushpin(this.selectedPin); } @@ -818,62 +806,58 @@ export class MapBox extends ViewBoxAnnotatableComponent this.allMapPushpins.map(doc => doc[Highlight]), - () => this.allMapPushpins.forEach(doc => { - - // if(doc[Highlight]){ - // this.deselectPin(); - // this.selectedPin = doc; - - // Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match"); - // Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match"); - - // this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin)); - // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), { - // color: 'green', - // }); - // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc)); - // this._bingMap.current.entities.push(newpin); - // this.map_docToPinMap.set(this.selectedPin, newpin); - - // MapAnchorMenu.Instance.Delete = this.deleteSelectedPin; - // MapAnchorMenu.Instance.Center = this.centerOnSelectedPin; - // MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation; - // } - // if (doc[Highlight]) { - // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); - // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), { - // color: 'orange', - // }); - // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); - // this._bingMap.current.entities.push(newpin); - // this.map_docToPinMap.set(doc, newpin); - - // } - // if (this.map_docToPinMap.get(doc).getColor() == 'orange' && !doc[Highlight]) { - // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); - // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {}); - // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); - // this._bingMap.current.entities.push(newpin); - // this.map_docToPinMap.set(doc, newpin); - - // } - // else if (this.map_docToPinMap.get(doc).getColor() != 'orange' && doc[Highlight]) { - // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); - // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), { - // color: 'orange', - // }); - // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); - // this._bingMap.current.entities.push(newpin); - // this.map_docToPinMap.set(doc, newpin); - // } - - - }) - , {fireImmediately: true}) + this._disposer.highlight = reaction( + () => this.allMapPushpins.map(doc => doc[Highlight]), + () => + this.allMapPushpins.forEach(doc => { + // if(doc[Highlight]){ + // this.deselectPin(); + // this.selectedPin = doc; + // Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match"); + // Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match"); + // this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin)); + // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), { + // color: 'green', + // }); + // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc)); + // this._bingMap.current.entities.push(newpin); + // this.map_docToPinMap.set(this.selectedPin, newpin); + // MapAnchorMenu.Instance.Delete = this.deleteSelectedPin; + // MapAnchorMenu.Instance.Center = this.centerOnSelectedPin; + // MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation; + // } + // if (doc[Highlight]) { + // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); + // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), { + // color: 'orange', + // }); + // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); + // this._bingMap.current.entities.push(newpin); + // this.map_docToPinMap.set(doc, newpin); + // } + // if (this.map_docToPinMap.get(doc).getColor() == 'orange' && !doc[Highlight]) { + // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); + // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {}); + // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); + // this._bingMap.current.entities.push(newpin); + // this.map_docToPinMap.set(doc, newpin); + // } + // else if (this.map_docToPinMap.get(doc).getColor() != 'orange' && doc[Highlight]) { + // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); + // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), { + // color: 'orange', + // }); + // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); + // this._bingMap.current.entities.push(newpin); + // this.map_docToPinMap.set(doc, newpin); + // } + }), + { fireImmediately: true } + ); // this.updateMapType(); this._disposer.location = reaction( () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }), -- cgit v1.2.3-70-g09d2 From 9154e0cfe279076d5148aad905846f18f2bab95e Mon Sep 17 00:00:00 2001 From: srichman333 Date: Sat, 26 Aug 2023 17:10:54 -0400 Subject: empty rows fix --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 9 +++++++-- src/client/views/nodes/DataVizBox/components/PieChart.tsx | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index b98f65054..b14525993 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -33,9 +33,14 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { static dataset = new ObservableMap(); private _vizRenderer: LineChart | Histogram | PieChart | undefined; - // all CSV records in the dataset + // all CSV records in the dataset (that aren't an empty row) @computed.struct get records() { - return DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); + var records = DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href) + var nonEmptyRecords = records?.filter(record => { + var notEmpty = false; + Object.keys(record).forEach(key => { if (record[key]!=undefined && record[key]!="") notEmpty = true}); + return notEmpty}) + return nonEmptyRecords; } // currently chosen visualization type: line, pie, histogram, table diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index de6263198..200e51240 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -52,7 +52,7 @@ export class PieChart extends React.Component { // organized by specified number percentages/ratios if one column is selected and it contains numbers // otherwise, assume data is organized by categories @computed get byCategory() { - return this.props.axes.length === 1 || !/\d/.test(this.props.records[0][this.props.axes[0]]); + return !/\d/.test(this.props.records[0][this.props.axes[0]]); } // filters all data to just display selected data if brushed (created from an incoming link) @computed get _pieChartData() { -- cgit v1.2.3-70-g09d2 From c973e8cbd4aabb545ea580b213e7f60d41cf2b20 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 26 Aug 2023 17:21:42 -0400 Subject: added pin highligting when following a link --- src/client/views/nodes/MapBox/MapBox.tsx | 102 +++++++++++++------------------ 1 file changed, 43 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 48bc02f84..62d622fd6 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 { Button, EditableText, IconButton, Type } from 'browndash-components'; +import { docs_v1 } from 'googleapis'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -11,7 +12,7 @@ import { Highlight, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { ScriptField } from '../../../../fields/ScriptField'; -import { NumCast, StrCast } from '../../../../fields/Types'; +import { DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; @@ -358,13 +359,16 @@ export class MapBox extends ViewBoxAnnotatableComponent { - const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); - if (note && this.selectedPin) { - note.latitude = this.selectedPin.latitude; - note.longitude = this.selectedPin.latitude; - } - }, 'create note annotation') + undoable( + action(() => { + const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); + if (note && this.selectedPin) { + note.latitude = this.selectedPin.latitude; + note.longitude = this.selectedPin.latitude; + } + }), + 'create note annotation' + ) ); }; sidebarDown = (e: React.PointerEvent) => { @@ -558,13 +562,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(this.selectedPin as Doc)); - this._bingMap.current.entities.push(newpin); - this.map_docToPinMap.set(this.selectedPin, newpin); + this.recolorPin(this.selectedPin, 'green'); MapAnchorMenu.Instance.Delete = this.deleteSelectedPin; MapAnchorMenu.Instance.Center = this.centerOnSelectedPin; @@ -653,9 +651,9 @@ export class MapBox extends ViewBoxAnnotatableComponent(); + map_pinHighlighted = new Map(); /* * Input: pin doc * Adds MicrosoftMaps Pushpin to the map (render) @@ -794,6 +793,22 @@ export class MapBox extends ViewBoxAnnotatableComponent { + this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin)); + this.map_docToPinMap.delete(pin); + const newpin = new this.MicrosoftMaps.Pushpin( + new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), + color + ? { + color, + } + : {} + ); + this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(pin)); + this._bingMap.current.entities.push(newpin); + this.map_docToPinMap.set(pin, newpin); + }; + /* * Called when BingMap is first rendered * Initializes starting values @@ -813,49 +828,18 @@ export class MapBox extends ViewBoxAnnotatableComponent this.allMapPushpins.map(doc => doc[Highlight]), () => - this.allMapPushpins.forEach(doc => { - // if(doc[Highlight]){ - // this.deselectPin(); - // this.selectedPin = doc; - // Doc.setDocFilter(this.rootDoc, "latitude", this.selectedPin.latitude, "match"); - // Doc.setDocFilter(this.rootDoc, "longitude", this.selectedPin.latitude, "match"); - // this._bingMap.current.entities.remove(this.map_docToPinMap.get(this.selectedPin)); - // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude), { - // color: 'green', - // }); - // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(this.selectedPin as Doc)); - // this._bingMap.current.entities.push(newpin); - // this.map_docToPinMap.set(this.selectedPin, newpin); - // MapAnchorMenu.Instance.Delete = this.deleteSelectedPin; - // MapAnchorMenu.Instance.Center = this.centerOnSelectedPin; - // MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation; - // } - // if (doc[Highlight]) { - // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); - // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), { - // color: 'orange', - // }); - // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); - // this._bingMap.current.entities.push(newpin); - // this.map_docToPinMap.set(doc, newpin); - // } - // if (this.map_docToPinMap.get(doc).getColor() == 'orange' && !doc[Highlight]) { - // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); - // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), {}); - // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); - // this._bingMap.current.entities.push(newpin); - // this.map_docToPinMap.set(doc, newpin); - // } - // else if (this.map_docToPinMap.get(doc).getColor() != 'orange' && doc[Highlight]) { - // this._bingMap.current.entities.remove(this.map_docToPinMap.get(doc)); - // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(doc.latitude, doc.longitude), { - // color: 'orange', - // }); - // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(doc)); - // this._bingMap.current.entities.push(newpin); - // this.map_docToPinMap.set(doc, newpin); - // } - }), + this.allMapPushpins + .map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })) + .filter(pair => pair.pushpin) + .forEach(({ doc, pushpin }) => { + if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) { + this.recolorPin(pushpin, 'orange'); + this.map_pinHighlighted.set(pushpin, true); + } else if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) { + this.recolorPin(pushpin); + this.map_pinHighlighted.delete(pushpin); + } + }), { fireImmediately: true } ); // this.updateMapType(); -- cgit v1.2.3-70-g09d2 From 10280b6f5474717d28fc2e4ea9ce7e9f622ba910 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Sat, 26 Aug 2023 21:07:37 -0400 Subject: select all / deselect all --- .../views/nodes/DataVizBox/components/Chart.scss | 17 +- .../views/nodes/DataVizBox/components/TableBox.tsx | 249 +++++++++++---------- 2 files changed, 148 insertions(+), 118 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss index 35e5187b2..9359919c6 100644 --- a/src/client/views/nodes/DataVizBox/components/Chart.scss +++ b/src/client/views/nodes/DataVizBox/components/Chart.scss @@ -76,9 +76,22 @@ fill: red; } } +.tableBox { + display: flex; + flex-direction: column; +} .table-container{ overflow: scroll; - margin: 10px; + margin: 5px; margin-left: 25px; - margin-top: 25px; + margin-right: 10px; + margin-bottom: 0; +} +.selectAll-buttons { + display: flex; + flex-direction: row; + justify-content: flex-end; + margin-top: 5px; + margin-right: 10px; + float: right; } \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 067dff07a..c416d7764 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -11,6 +11,7 @@ import { LinkManager } from '../../../../util/LinkManager'; import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; import './Chart.scss'; +import { Button, Type } from 'browndash-components'; interface TableBoxProps { rootDoc: Doc; @@ -64,128 +65,144 @@ export class TableBox extends React.Component { const selected = NumListCast(this.props.layoutDoc.dataViz_selectedRows); this.props.layoutDoc.dataViz_selectedRows = new List(selected.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data const highlighted = NumListCast(this.props.layoutDoc.dataViz_highlitedRows); - this.props.layoutDoc.dataViz_highlitedRows = new List(highlighted.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data + this.props.layoutDoc.dataViz_highlitedRows = new List(highlighted.filter(rowId => this._tableDataIds.includes(rowId))); // filters through highlighted to remove guids that were removed in the incoming data }; render() { if (this._tableData.length > 0) { return ( -
- r?.addEventListener( - 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) - (e: WheelEvent) => { - if (!r.scrollTop && e.deltaY <= 0) e.preventDefault(); - e.stopPropagation(); - }, - { passive: false } - ) - }> - - - - {this.columns.map(col => ( - - ))} - - - - {this._tableDataIds - ?.map(rowId => ({ record: this.props.records[rowId], rowId })) - .map(({ record, rowId }) => ( - { - const highlited = Cast(this.props.layoutDoc.dataViz_highlitedRows, listSpec('number'), null); - const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('number'), null); - if (e.metaKey) { - // highlighting a row - if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1); - else highlited?.push(rowId); - if (!selected?.includes(rowId)) selected?.push(rowId); - } else { - // selecting a row - if (selected?.includes(rowId)) { +
+
+
+
+ r?.addEventListener( + 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) + (e: WheelEvent) => { + if (!r.scrollTop && e.deltaY <= 0) e.preventDefault(); + e.stopPropagation(); + }, + { passive: false } + ) + }> +
{ - const downX = e.clientX; - const downY = e.clientY; - setupMoveUpEvents( - {}, - e, - e => { - // dragging off a column to create a brushed DataVizBox - const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; - const targetCreator = (annotationOn: Doc | undefined) => { - const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); - embedding._dataViz = DataVizView.TABLE; - embedding._dataViz_axes = new List([col, col]); - embedding._dataViz_parentViz = this.props.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); - return embedding; - }; - if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { - DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { - dragComplete: e => { - if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { - e.linkDocument.link_displayLine = true; - e.linkDocument.link_matchEmbeddings = true; - // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; - // e.annoDragData.linkSourceDoc.followLinkZoom = false; - } - }, - }); - return true; - } - return false; - }, - emptyFunction, - action(e => { - const newAxes = this.props.axes; - if (newAxes.includes(col)) newAxes.splice(newAxes.indexOf(col), 1); - else if (newAxes.length > 1) newAxes[1] = col; - else newAxes.push(col); - this.props.selectAxes(newAxes); - }) - ); - }}> - {col} -
+ + + {this.columns.map(col => ( + + ))} + + + + {this._tableDataIds + ?.map(rowId => ({ record: this.props.records[rowId], rowId })) + .map(({ record, rowId }) => ( + { + const highlited = Cast(this.props.layoutDoc.dataViz_highlitedRows, listSpec('number'), null); + const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('number'), null); + if (e.metaKey) { + // highlighting a row if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1); - selected.splice(selected.indexOf(rowId), 1); - } else selected?.push(rowId); - } - e.stopPropagation(); - })} - style={{ - background: NumListCast(this.props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '', - width: '110%', - }}> - {this.columns.map(col => { - // each cell - const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false; - return ( - - ); - })} - - ))} - -
{ + const downX = e.clientX; + const downY = e.clientY; + setupMoveUpEvents( + {}, + e, + e => { + // dragging off a column to create a brushed DataVizBox + const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; + const targetCreator = (annotationOn: Doc | undefined) => { + const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); + embedding._dataViz = DataVizView.TABLE; + embedding._dataViz_axes = new List([col, col]); + embedding._dataViz_parentViz = this.props.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); + return embedding; + }; + if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { + DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { + dragComplete: e => { + if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { + e.linkDocument.link_displayLine = true; + e.linkDocument.link_matchEmbeddings = true; + // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; + // e.annoDragData.linkSourceDoc.followLinkZoom = false; + } + }, + }); + return true; + } + return false; + }, + emptyFunction, + action(e => { + const newAxes = this.props.axes; + if (newAxes.includes(col)) newAxes.splice(newAxes.indexOf(col), 1); + else if (newAxes.length > 1) newAxes[1] = col; + else newAxes.push(col); + this.props.selectAxes(newAxes); + }) + ); + }}> + {col} +
- {record[col]} -
+ else highlited?.push(rowId); + if (!selected?.includes(rowId)) selected?.push(rowId); + } else { + // selecting a row + if (selected?.includes(rowId)) { + if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1); + selected.splice(selected.indexOf(rowId), 1); + } else selected?.push(rowId); + } + e.stopPropagation(); + })} + style={{ + background: NumListCast(this.props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '', + width: '110%', + }}> + {this.columns.map(col => { + // each cell + const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false; + return ( + + {record[col]} + + ); + })} + + ))} + + +
); } else -- cgit v1.2.3-70-g09d2 From 0a813fdf7d73018ad5248d87fecbd9e55f3dc2d7 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 26 Aug 2023 21:25:24 -0400 Subject: many fixes to map search bar, dragging pushpin, highlighting pushpins on link following --- src/client/documents/Documents.ts | 8 +- src/client/views/nodes/MapBox/MapBox.tsx | 552 +++++++++--------------------- src/client/views/nodes/MapBox/MapBox2.tsx | 4 +- src/client/views/nodes/trails/PresBox.tsx | 16 +- 4 files changed, 170 insertions(+), 410 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index af8cc07ed..d5d6fb2ba 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -158,6 +158,7 @@ export class DocumentOptions { _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); latitude?: NUMt = new NumInfo('latitude coordinate for map views'); longitude?: NUMt = new NumInfo('longitude coordinate for map views'); + map?: STRt = new StrInfo('text location of map'); _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)'); _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden'); _width?: NUMt = new NumInfo('displayed width of a document'); @@ -295,8 +296,9 @@ export class DocumentOptions { config_latitude?: NUMt = new NumInfo('latitude of a map'); // latitude of a map config_longitude?: NUMt = new NumInfo('longitude of map'); // longitude of map - config_mapZoom?: NUMt = new NumInfo('zoom of map'); // zoom of map - config_mapType?: string; + config_map_zoom?: NUMt = new NumInfo('zoom of map'); // zoom of map + config_map_type?: string; + config_map?: string; config_panX?: NUMt = new NumInfo('panX saved as a view spec'); config_panY?: NUMt = new NumInfo('panY saved as a view spec'); config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec'); @@ -527,7 +529,7 @@ export namespace Docs { DocumentType.MAP, { layout: { view: MapBox, dataField: defaultDataKey }, - options: { _height: 600, _width: 800, nativeDimModifiable: true, systemIcon: 'BsFillPinMapFill' }, + options: { map: '', _height: 600, _width: 800, nativeDimModifiable: true, systemIcon: 'BsFillPinMapFill' }, }, ], [ diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 62d622fd6..94944b83d 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,19 +1,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; import { Button, EditableText, IconButton, Type } from 'browndash-components'; -import { docs_v1 } from 'googleapis'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { TbHeartMinus } from 'react-icons/tb'; import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; import { Highlight, Width } from '../../../../fields/DocSymbols'; -import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; -import { ScriptField } from '../../../../fields/ScriptField'; import { DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; @@ -24,9 +19,8 @@ import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; -import { Annotation } from '../../pdf/Annotation'; import { SidebarAnnos } from '../../SidebarAnnos'; -import { DocumentView, OpenWhere } from '../DocumentView'; +import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps, PresBox } from '../trails'; import { MapAnchorMenu } from './MapAnchorMenu'; @@ -45,29 +39,8 @@ import './MapBox.scss'; * 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 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 */ @@ -85,169 +58,49 @@ const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing // }); @observer -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)); +export class MapBox extends ViewBoxAnnotatableComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapBox, fieldKey); } - public get SidebarKey() { - return this.fieldKey + '_sidebar'; - } + private _mainCont: React.RefObject = React.createRef(); + private _annotationLayer: React.RefObject = React.createRef(); + private _sidebarRef = React.createRef(); + private _ref: React.RefObject = React.createRef(); + private _disposers: { [key: string]: IReactionDisposer } = {}; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); - @computed get inlineTextAnnotations() { - return this.allMapMarkers.filter(a => a.text_inlineAnnotations); - } - @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 inputRef = React.createRef(); - @observable private searchMarkers: google.maps.Marker[] = []; - @observable private searchBox = undefined as any; // new window.google.maps.places.Autocomplete(this.inputRef.current!, options); @observable private _savedAnnotations = new ObservableMap(); + @computed get inlineTextAnnotations() { + return this.allMapMarkers.filter(a => a.text_inlineAnnotations); + } @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(); - @computed get SidebarShown() { return this.layoutDoc._layout_showSidebar ? true : false; } + @computed get sidebarWidthPercent() { + return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); + } + @computed get sidebarColor() { + return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4')); + } + @computed get SidebarKey() { + return this.fieldKey + '_sidebar'; + } - static _canAnnotate = true; - static _hadSelection: boolean = false; - private _sidebarRef = React.createRef(); - private _ref: React.RefObject = React.createRef(); - private _disposer: { [key: string]: IReactionDisposer } = {}; componentDidMount() { this.props.setContentView?.(this); } componentWillUnmount(): void { this.deselectPin(); - Object.keys(this._disposer).forEach(key => this._disposer[key]?.()); + Object.keys(this._disposers).forEach(key => this._disposers[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(); - 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())); - }; - - /** - * 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; - }; - - /** - * 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.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); - this.addDocument(mapMarker, this.annotationKey); - }; - - _loadPending = true; - - @action - centered = () => { - if (this._loadPending && this._map.getBounds()) { - this._loadPending = false; - this.layoutDoc.freeform_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.freeform_fitContentsToBox && this.fitBounds(this._map); - } - this.dataDoc.mapZoom = this._map.getZoom(); - }; - - /** - * 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, - }) - ); - }; /** * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts @@ -256,7 +109,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { - console.log('print all sidebar Docs'); if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar(); const docs = doc instanceof Doc ? [doc] : doc; docs.forEach(doc => { @@ -280,11 +132,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { - // if (this.layoutDoc._layout_showSidebar) this.toggleSidebar(); - const docs = doc instanceof Doc ? [doc] : doc; - return this.removeDocument(doc, sidebarKey); - }; + sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeDocument(doc, sidebarKey); /** * Toggle sidebar onclick the tiny comment button on the top right corner @@ -317,14 +165,7 @@ export class MapBox extends ViewBoxAnnotatableComponent UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map') ); }; - - sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); - @computed get layout_sidebarWidthPercent() { - return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); - } - @computed get sidebarColor() { - return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4')); - } + sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); /** * Handles toggle of sidebar on click the little comment button @@ -349,27 +190,27 @@ export class MapBox extends ViewBoxAnnotatableComponent { - //1.2 * w * ? = .2 * w .2/1.2 const prevWidth = this.sidebarWidth(); this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); }; createNoteAnnotation = () => { - !this.layoutDoc.layout_showSidebar && this.toggleSidebar(); - - setTimeout( - undoable( - action(() => { - const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); - if (note && this.selectedPin) { - note.latitude = this.selectedPin.latitude; - note.longitude = this.selectedPin.latitude; - } - }), - 'create note annotation' - ) + const createFunc = undoable( + action(() => { + const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); + if (note && this.selectedPin) { + note.latitude = this.selectedPin.latitude; + note.longitude = this.selectedPin.longitude; + note.map = this.selectedPin.map; + } + }), + 'create note annotation' ); + if (!this.layoutDoc.layout_showSidebar) { + this.toggleSidebar(); + setTimeout(createFunc); + } else createFunc(); }; sidebarDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); @@ -406,37 +247,10 @@ export class MapBox extends ViewBoxAnnotatableComponent { - return this.addDocument(doc, annotationKey); - }; + addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey); pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none'); - @computed get annotationLayer() { - return ( -
- {this.inlineTextAnnotations - .sort((a, b) => NumCast(a.y) - NumCast(b.y)) - .map(anno => ( - - ))} -
- ); - } - - // 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 - * @returns - */ - private renderMarkers = () => { - return this.allMapMarkers.map(place => ( - this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} /> - )); - }; - 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)); @@ -473,25 +287,15 @@ export class MapBox extends ViewBoxAnnotatableComponent { - res(r.results[0].location); - }), + callback: action((r: any) => res(r.results[0].location)), errorCallback: (e: any) => reject(), }); } }); }; - /** - * - * - * ERIC'S BING MAP CODE BELOW - * - * - * - **/ @observable - bingSearchBarContents: any = 'enter city/zip/...'; // For Bing Maps: The contents of the Bing search bar (string) + bingSearchBarContents: any = this.rootDoc.map; // For Bing Maps: The contents of the Bing search bar (string) geoDataRequestOptions = { entityType: 'PopulatedPlace', @@ -502,14 +306,14 @@ export class MapBox extends ViewBoxAnnotatableComponent { + createPushpin = undoable((latitude: number, longitude: number, map?: string) => { // Stores the pushpin as a MapMarkerDocument const mapMarker = Docs.Create.PushpinDocument( NumCast(latitude), NumCast(longitude), false, [], - {} + { map: map } // ,'pushpinIDamongus'+ this.incrementer++ ); this.addDocument(mapMarker, this.annotationKey); @@ -517,18 +321,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { - // if (pinDoc) this.removePushpin(pinDoc); - // else this._bingMap.current.entities.remove(pin); - // }; - // The pin that is selected - @observable - selectedPin: Doc | undefined; + @observable selectedPin: Doc | undefined; @action deselectPin = () => { @@ -544,6 +338,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { this.deselectPin(); this.selectedPin = pinDoc; + this.bingSearchBarContents = pinDoc.map; Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match'); - Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.latitude, 'match'); + Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match'); this.recolorPin(this.selectedPin, 'green'); @@ -575,7 +371,6 @@ export class MapBox extends ViewBoxAnnotatableComponent this.deselectPin(); + mapOnClick = (e: { location: { latitude: any; longitude: any } }) => { + this.props.select(false); + this.deselectPin(); + }; /* * 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; - this.dataDoc.mapZoom = this._bingMap.current.getZoom(); - // if(this.dataDoc.mapType == 'x'){ - // this.dataDoc.locationToLookAt - // } - // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId(); + mapRecentered = () => { + if ( + Math.abs(NumCast(this.dataDoc.latitude) - this._bingMap.current.getCenter().latitude) > 1e-7 || // + Math.abs(NumCast(this.dataDoc.longitude) - this._bingMap.current.getCenter().longitude) > 1e-7 + ) { + this.dataDoc.latitude = this._bingMap.current.getCenter().latitude; + this.dataDoc.longitude = this._bingMap.current.getCenter().longitude; + this.dataDoc.map = ''; + this.bingSearchBarContents = ''; + } + this.dataDoc.map_zoom = this._bingMap.current.getZoom(); }; /* * Updates maptype */ @action - updateMapType = () => { - this.dataDoc.mapType = this._bingMap.current.getMapTypeId(); - }; + updateMapType = () => (this.dataDoc.map_type = this._bingMap.current.getMapTypeId()); - searched_pin: any; /* * 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 () => { - const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); - this.dataDoc.latitude = location.latitude; - this.dataDoc.longitude = location.longitude; - this.dataDoc.mapZoom = this._bingMap.current.getZoom(); - // Creates a temporary pin but does not add it to the dataDoc - this.createPushpin(this.dataDoc.latitude, this.dataDoc.longitude); + bingSearch = () => { + return this.bingGeocode(this._bingMap, this.bingSearchBarContents).then(location => { + this.dataDoc.latitude = location.latitude; + this.dataDoc.longitude = location.longitude; + this.dataDoc.map_zoom = this._bingMap.current.getZoom(); + this.dataDoc.map = this.bingSearchBarContents; + }); }; - /** - * 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.addPushpin(pin)); - // }; - /* * Returns doc w/ relevant info */ @@ -644,12 +432,12 @@ export class MapBox extends ViewBoxAnnotatableComponent { - // this.allMapPushpins - // this.allMapPushpins.map(pin => this.addPushpin(pin)); - // this._bingMap.current.entities.clear(); + removePushpin = (pinDoc: Doc) => this.removeDocument(pinDoc, this.annotationKey); - this.removeDocument(pinDoc, this.annotationKey); - - // this.dataDoc[this.annotationKey] - }; /* * Removes pushpin from map render */ @@ -725,11 +503,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { let target = document.elementFromPoint(e.x, e.y); - - while (target != null) { - if (target === MapAnchorMenu.top.current) { - return; - } + while (target) { + if (target === MapAnchorMenu.top.current) return; target = target.parentElement; } e.stopPropagation(); @@ -738,13 +513,13 @@ export class MapBox extends ViewBoxAnnotatableComponent if( e.parent... == mapanchormenu.top.currrent) do nothing; else hide menu - @action centerOnSelectedPin = () => { if (this.selectedPin) { this.dataDoc.latitude = this.selectedPin.latitude; this.dataDoc.longitude = this.selectedPin.longitude; + this.dataDoc.map = this.selectedPin.map ?? ''; + this.bingSearchBarContents = this.selectedPin.map; } MapAnchorMenu.Instance.fadeOut(true); document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu); @@ -777,33 +552,13 @@ export class MapBox extends ViewBoxAnnotatableComponent { - if (this.placePinOn) this.placePinOn = false; - else this.placePinOn = true; - }; - @action - searchbarOnEdit = (newText: string) => { - this.bingSearchBarContents = newText; - }; + searchbarOnEdit = (newText: string) => (this.bingSearchBarContents = newText); recolorPin = (pin: Doc, color?: string) => { this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin)); this.map_docToPinMap.delete(pin); - const newpin = new this.MicrosoftMaps.Pushpin( - new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), - color - ? { - color, - } - : {} - ); + const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), color ? { color } : {}); this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(pin)); this._bingMap.current.entities.push(newpin); this.map_docToPinMap.set(pin, newpin); @@ -822,29 +577,36 @@ export class MapBox extends ViewBoxAnnotatableComponent this.rootDoc.map, + mapLoc => (this.bingSearchBarContents = mapLoc), + { fireImmediately: true } + ); + this._disposers.highlight = reaction( () => this.allMapPushpins.map(doc => doc[Highlight]), - () => - this.allMapPushpins - .map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })) - .filter(pair => pair.pushpin) - .forEach(({ doc, pushpin }) => { - if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) { - this.recolorPin(pushpin, 'orange'); - this.map_pinHighlighted.set(pushpin, true); - } else if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) { - this.recolorPin(pushpin); - this.map_pinHighlighted.delete(pushpin); - } - }), + () => { + const allPins = this.allMapPushpins.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin); + allPins.forEach(({ doc, pushpin }) => { + if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) { + this.recolorPin(pushpin); + this.map_pinHighlighted.delete(pushpin); + } + }); + allPins.forEach(({ doc, pushpin }) => { + if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) { + this.recolorPin(pushpin, 'orange'); + this.map_pinHighlighted.set(pushpin, true); + } + }); + }, { fireImmediately: true } ); - // this.updateMapType(); - this._disposer.location = reaction( - () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }), + + this._disposers.location = reaction( + () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.map_zoom, mapType: this.rootDoc.map_type }), locationObject => { // if (this._bingMap.current) try { @@ -862,43 +624,50 @@ 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'); - 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); + let dragClone: HTMLDivElement | undefined; - 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() - this.sidebarWidth()) / 2; - const y = cpt[1] - 32 /* height of search bar */ - this.props.PanelHeight() / 2; - const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y)); - this.createPushpin(location.latitude, location.longitude); - break; + setupMoveUpEvents( + e, + e, + e => { + if (!dragClone) { + dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement; + dragClone.style.position = 'absolute'; + dragClone.style.zIndex = '10000'; + DragManager.Root().appendChild(dragClone); + } + dragClone.style.transform = `translate(${e.clientX - 15}px, ${e.clientY - 15}px)`; + return false; + }, + e => { + if (!dragClone) return; + DragManager.Root().removeChild(dragClone); + let target = document.elementFromPoint(e.x, e.y); + while (target) { + if (target === this._ref.current) { + const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); + const x = cpt[0] - (this.props.PanelWidth() - this.sidebarWidth()) / 2; + const y = cpt[1] - 32 /* height of search bar */ - this.props.PanelHeight() / 2; + const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y)); + this.createPushpin(location.latitude, location.longitude); + break; + } + target = target.parentElement; + } + }, + e => { + const createPin = () => this.createPushpin(this.rootDoc.latitude, this.rootDoc.longitude, this.rootDoc.map); + if (this.bingSearchBarContents) { + this.bingSearch().then(createPin); + } else createPin(); } - target = target.parentElement; - } + ); }; - searchbarKeyDown = (e: any) => { - if (e.key === 'Enter') { - this.bingSearch(); - } - }; + searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch(); + + _dragRef = React.createRef(); render() { const renderAnnotations = (childFilters?: () => string[]) => null; return ( @@ -908,26 +677,18 @@ export class MapBox 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.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> + style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
{renderAnnotations(this.transparentFilter)}
{renderAnnotations(this.opaqueFilter)} {SnappingManager.GetIsDragging() ? null : renderAnnotations()} - {this.annotationLayer}
typeof newText === 'string' && this.searchbarOnEdit(newText)} onEnter={e => this.bingSearch()} - placeholder={this.bingSearchBarContents} + placeholder={this.bingSearchBarContents || 'enter city/zip/...'} textAlign="center" /> } onClick={this.bingSearch} - onDoubleClick={function noRefCheck() {}} - onPointerDown={function noRefCheck() {}} - onPointerDownCapture={function noRefCheck() {}} - onPointerMove={function noRefCheck() {}} - onPointerMoveCapture={function noRefCheck() {}} - onPointerUp={function noRefCheck() {}} type={Type.TERT} /> -
+
(this.draggingPin = false)}> + />
{!this._mapReady ? null : this.allMapPushpins.map(pushpin => ( new ScriptField(undefined)} onKey={undefined} onDoubleClick={undefined} onBrowseClick={undefined} @@ -983,7 +737,7 @@ export class MapBox extends ViewBoxAnnotatableComponent new Transform(0, 0, 0)} + ScreenToLocalTransform={Transform.Identity} fitContentsToBox={undefined} focus={returnOne} /> @@ -1019,7 +773,7 @@ export class MapBox extends ViewBoxAnnotatableComponent {/* */} -
+
{ if (this._loadPending && this._map.getBounds()) { @@ -292,7 +292,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent() { Doc.SetInPlace(bestTarget, 'longitude', NumCast(activeItem.config_longitude), true); changed = true; } - if (bestTarget.zoom !== activeItem.config_mapZoom) { - Doc.SetInPlace(bestTarget, 'mapZoom', NumCast(activeItem.config_mapZoom), true); + if (bestTarget.zoom !== activeItem.config_map_zoom) { + Doc.SetInPlace(bestTarget, 'map_zoom', NumCast(activeItem.config_map_zoom), true); changed = true; } - if (bestTarget.mapType !== activeItem.config_mapType) { - Doc.SetInPlace(bestTarget, 'mapType', StrCast(activeItem.config_mapType), true); + if (bestTarget.map_type !== activeItem.config_map_type) { + Doc.SetInPlace(bestTarget, 'map_type', StrCast(activeItem.config_map_type), true); + changed = true; + } + if (bestTarget.map !== activeItem.config_map) { + Doc.SetInPlace(bestTarget, 'map', StrCast(activeItem.config_map), true); changed = true; } } @@ -663,8 +667,8 @@ export class PresBox extends ViewBoxBaseComponent() { if (pinProps.pinData.map) { // pinDoc.config_latitude = targetDoc?.latitude; // pinDoc.config_longitude = targetDoc?.longitude; - pinDoc.config_mapZoom = targetDoc?.mapZoom; - pinDoc.config_mapType = targetDoc?.mapType; + pinDoc.config_map_zoom = targetDoc?.map_zoom; + pinDoc.config_map_type = targetDoc?.map_type; //... } if (pinProps.pinData.poslayoutview) -- cgit v1.2.3-70-g09d2 From a142ff5e6ce017686e9bf418c502417d8cea5cad Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 26 Aug 2023 22:48:39 -0400 Subject: fixed map anchors to be configs, not maps! fixed doc decorations to not allow clicking on them when showNothing is set. increased font size of map search. --- src/client/documents/Documents.ts | 3 --- src/client/views/DocumentDecorations.tsx | 8 +++++--- src/client/views/nodes/MapBox/MapBox.scss | 1 + src/client/views/nodes/MapBox/MapBox.tsx | 25 ++++++++++++++++++++++--- 4 files changed, 28 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d5d6fb2ba..9ae55d7f9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1108,9 +1108,6 @@ export namespace Docs { id ); } - export function MapanchorDocument(options: DocumentOptions = {}, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.MAP), options?.data, options, id); - } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Linear }, id); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index f3daf3ffa..90425f264 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -80,8 +80,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P // show decorations whenever pointer moves outside of selection bounds. 'pointermove', action(e => { - if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX || this.Bounds.r < e.clientX || this.Bounds.y > e.clientY || this.Bounds.b < e.clientY)) { - this._showNothing = false; + if (this.Bounds.x || this.Bounds.y || this.Bounds.r || this.Bounds.b) { + if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX || this.Bounds.r < e.clientX || this.Bounds.y > e.clientY || this.Bounds.b < e.clientY)) { + this._showNothing = false; + } } }) ); @@ -885,7 +887,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P ); return ( -
+
(); private _mainCont: React.RefObject = React.createRef(); private _annotationLayer: React.RefObject = React.createRef(); private _sidebarRef = React.createRef(); @@ -430,7 +431,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER - const anchor = Docs.Create.MapanchorDocument({ + const anchor = Docs.Create.ConfigDocument({ title: 'MapAnchor:' + this.rootDoc.title, text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location', config_latitude: NumCast(this.selectedPin?.latitude ?? this.dataDoc.latitude), @@ -667,8 +668,26 @@ export class MapBox extends ViewBoxAnnotatableComponent e.key === 'Enter' && this.bingSearch(); - _dragRef = React.createRef(); + static _firstRender = true; + static _rerenderDelay = 0; + _rerenderTimeout: any; render() { + // bcz: no idea what's going on here, but bings maps have some kind of bug + // such that we need to delay rendering a second map on startup until the first map is rendered. + this.rootDoc[DocCss]; + if (MapBox._firstRender) { + MapBox._firstRender = false; + MapBox._rerenderDelay = 500; + } else if (MapBox._rerenderDelay) { + // prettier-ignore + this._rerenderTimeout = this._rerenderTimeout ?? + setTimeout(action(() => { + MapBox._rerenderDelay = 0; + this.rootDoc[DocCss] = this.rootDoc[DocCss] + 1; + }), MapBox._rerenderDelay); + return null; + } + const renderAnnotations = (childFilters?: () => string[]) => null; return (
-- cgit v1.2.3-70-g09d2 From e6d135c656645873eeb477974a345d62b17bc9cd Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 26 Aug 2023 22:50:09 -0400 Subject: from last --- src/client/views/nodes/MapBox/MapPushpinBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx index 66fe1ce53..552bceace 100644 --- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx +++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx @@ -30,6 +30,6 @@ export class MapPushpinBox extends ViewBoxBaseComponent() { } render() { - return
; + return
; } } -- cgit v1.2.3-70-g09d2 From ffb910a768c75df57e9d7bca15aca67e9216b025 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 26 Aug 2023 22:53:27 -0400 Subject: from last --- src/client/documents/Documents.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9ae55d7f9..add12896e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -159,6 +159,8 @@ export class DocumentOptions { latitude?: NUMt = new NumInfo('latitude coordinate for map views'); longitude?: NUMt = new NumInfo('longitude coordinate for map views'); map?: STRt = new StrInfo('text location of map'); + map_type?: STRt = new StrInfo('type of map view'); + map_zoom?: NUMt = new NumInfo('zoom of a map view'); _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)'); _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden'); _width?: NUMt = new NumInfo('displayed width of a document'); @@ -255,7 +257,6 @@ export class DocumentOptions { dontPlayLinkOnSelect?: BOOLt = new BoolInfo('whether an audio/video should start playing when a link is followed to it.'); openFactoryLocation?: string; // an OpenWhere value to place the factory created document openFactoryAsDelegate?: boolean; // - zoom?: NUMt = new NumInfo('zoom of a mapping view'); updateContentsScript?: ScriptField; // reactive script invoked when viewing a document that can update contents of a collection (or do anything) toolTip?: string; // tooltip to display on hover toolType?: string; // type of pen tool -- cgit v1.2.3-70-g09d2 From bf1777b93be0707e17e3b3c0ca6c965facebfe14 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 02:50:01 -0400 Subject: fixed filters to filter by linkedTo instead of lat/lng. made links to pushpins when anything is added to sidebar and pushpin is selected --- src/client/documents/Documents.ts | 32 +++++--- src/client/views/MainView.tsx | 40 +++++++++- src/client/views/SidebarAnnos.tsx | 8 +- src/client/views/nodes/MapBox/MapBox.tsx | 127 ++++++++++++++++--------------- src/client/views/pdf/PDFViewer.tsx | 2 +- src/fields/Doc.ts | 6 +- src/fields/documentSchemas.ts | 11 ++- 7 files changed, 140 insertions(+), 86 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index add12896e..ceee8c76f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1075,8 +1075,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.MAP), new List(documents), options); } - export function PushpinDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { latitude: lat, longitude: lng, infoWindowOpen, ...options }, id); + export function PushpinDocument(latitude: number, longitude: number, infoWindowOpen: boolean, documents: Array, options: DocumentOptions, id?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { latitude, longitude, infoWindowOpen, ...options }, id); } // shouldn't ever need to create a KVP document-- instead set the LayoutTemplateString to be a KeyValueBox for the DocumentView (see addDocTab in TabDocView) @@ -1097,9 +1097,6 @@ export namespace Docs { export function HTMLMarkerDocument(documents: Array, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Freeform }, id); } - export function MapMarkerDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { latitude: lat, longitude: lng, infoWindowOpen, ...options, _type_collection: CollectionViewType.Freeform }, id); - } export function PileDocument(documents: Array, options: DocumentOptions, id?: string) { return InstanceFromProto( @@ -1274,10 +1271,23 @@ export namespace DocUtils { if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { return false; } - for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(Doc.FilterSep)[0])) { const facet = filterFacets[facetKey]; + let links = true; + const linkedTo = filterFacets['-linkedTo'] && Array.from(Object.keys(filterFacets['-linkedTo']))?.[0]; + const linkedToField = filterFacets['-linkedTo']?.[linkedTo]; + const allLinks = linkedTo && linkedToField ? LinkManager.Instance.getAllRelatedLinks(d) : []; + // prettier-ignore + if (linkedTo) { + if (allLinks.some(d => linkedTo === Field.toScriptString(DocCast(DocCast(d.link_anchor_1)?.[linkedToField]))) || // + allLinks.some(d => linkedTo === Field.toScriptString(DocCast(DocCast(d.link_anchor_2)?.[linkedToField])))) + { + links = true; + } + else links = false + } + // facets that match some value in the field of the document (e.g. some text field) const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match'); @@ -1293,7 +1303,7 @@ export namespace DocUtils { // facets that have an x next to them const xs = Object.keys(facet).filter(value => facet[value] === 'x'); - if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; + if (!linkedTo && !unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value)); const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value)); const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined); @@ -1312,11 +1322,11 @@ export namespace DocUtils { }); // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria if (parentCollection?.childFilters_boolean === 'OR') { - if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; + if (links && satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; } // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria else { - if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; + if (!links || !satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; } } return parentCollection?.childFilters_boolean === 'OR' ? false : true; @@ -1800,8 +1810,8 @@ export namespace DocUtils { const longitude = result.exifData?.data?.GPSLongitude; const longitudeDirection = result.exifData?.data?.GPSLongitudeRef; if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) { - proto.lat = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); - proto.lng = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); + proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); + proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); } } if (Upload.isVideoInformation(result)) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 344a401f3..2a9f0cc02 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -60,6 +60,7 @@ import { ImageBox } from './nodes/ImageBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview } from './nodes/LinkDocPreview'; import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu'; +import { MapBox } from './nodes/MapBox/MapBox'; import { RadialMenu } from './nodes/RadialMenu'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; import { OverlayView } from './OverlayView'; @@ -956,6 +957,42 @@ export class MainView extends React.Component { @computed get linkDocPreview() { return LinkDocPreview.LinkInfo ? : null; } + @observable mapBoxHackBool = false; + @computed get mapBoxHack() { + return this.mapBoxHackBool ? null : ( + r && (this.mapBoxHackBool = true))} + fieldKey="data" + select={returnFalse} + isSelected={returnFalse} + Document={this.headerBarDoc} + DataDoc={undefined} + addDocTab={returnFalse} + pinToPres={emptyFunction} + docViewPath={returnEmptyDoclist} + styleProvider={DefaultStyleProvider} + rootSelected={returnTrue} + addDocument={returnFalse} + removeDocument={returnFalse} + fitContentsToBox={returnTrue} + isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events) + isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive + ScreenToLocalTransform={Transform.Identity} + childHideResizeHandles={returnTrue} + childDragAction="move" + dontRegisterView={true} + PanelWidth={this.headerBarDocWidth} + PanelHeight={this.headerBarDocHeight} + renderDepth={0} + focus={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} + bringToFront={emptyFunction} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + /> + ); + } render() { return ( @@ -1011,7 +1048,7 @@ export class MainView extends React.Component { - + @@ -1020,6 +1057,7 @@ export class MainView extends React.Component { {this.snapLines} + {this.mapBoxHack} {/* */} diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 520485a71..c48a7241a 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -66,7 +66,7 @@ export class SidebarAnnos extends React.Component { return '_' + this.sidebarKey + '_childFilters'; } - anchorMenuClick = (anchor: Doc) => { + anchorMenuClick = (anchor: Doc, filterExlusions?: string[]) => { const startup = StrListCast(this.props.rootDoc.childFilters) .map(filter => filter.split(':')[0]) .join(' '); @@ -79,6 +79,7 @@ export class SidebarAnnos extends React.Component { _layout_autoHeight: true, _text_fontSize: StrCast(Doc.UserDoc().fontSize), _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), + target: 'HELLO' as any, }); FormattedTextBox.SelectOnLoad = target[Id]; FormattedTextBox.DontSelectInitialText = true; @@ -87,7 +88,7 @@ export class SidebarAnnos extends React.Component { const taggedContent = this.childFilters() .filter(data => data.split(':')[0]) - .filter(data => data.split(':')[0] !== 'latitude' && data.split(':')[0] !== 'longitude') + .filter(data => !filterExlusions?.includes(data.split(':')[0])) .map(data => { const key = data.split(':')[0]; const val = Field.Copy(this.allMetadata.get(key)); @@ -188,7 +189,7 @@ export class SidebarAnnos extends React.Component { }; render() { const renderTag = (tag: string) => { - const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`tags:${tag}:check`); + const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`tags::${tag}::check`); return (
Doc.setDocFilter(this.props.rootDoc, 'tags', tag, 'check', true, this.sidebarKey, e.shiftKey)}> {tag} @@ -231,7 +232,6 @@ export class SidebarAnnos extends React.Component { .map(key => renderMeta(key, this.allMetadata.get(key)))} */}
-
(); - @computed get inlineTextAnnotations() { - return this.allMapMarkers.filter(a => a.text_inlineAnnotations); - } @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); } - @computed get allMapMarkers() { + // this list contains pushpins and configs + @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } + @computed get allPushpins() { + return this.allAnnotations.filter(anno => anno.type === DocumentType.PUSHPIN); + } @computed get SidebarShown() { return this.layoutDoc._layout_showSidebar ? true : false; } @@ -113,14 +116,20 @@ export class MapBox extends ViewBoxAnnotatableComponent { - if (doc.lat !== undefined && doc.lng !== undefined) { - const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng); - if (existingMarker) { - Doc.AddDocToList(existingMarker, 'data', doc); - } else { - const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {}); - this.addDocument(marker, this.annotationKey); - } + let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPin; + if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) { + existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map)); + } + if (existingPin) { + setTimeout(() => { + // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet + if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { + const anchor = this.getAnchor(true, undefined, existingPin); + anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to map location' }); + doc.latitude = existingPin?.latitude; + doc.longitude = existingPin?.longitude; + } + }); } }); //add to annotation list @@ -199,7 +208,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { const createFunc = undoable( action(() => { - const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); + const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false), ['latitude', 'longitude', '-linkedTo']); if (note && this.selectedPin) { note.latitude = this.selectedPin.latitude; note.longitude = this.selectedPin.longitude; @@ -309,7 +318,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { // Stores the pushpin as a MapMarkerDocument - const mapMarker = Docs.Create.PushpinDocument( + const pushpin = Docs.Create.PushpinDocument( NumCast(latitude), NumCast(longitude), false, @@ -317,8 +326,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps, existingPin?: Doc) => { /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.ConfigDocument({ title: 'MapAnchor:' + this.rootDoc.title, text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location', - config_latitude: NumCast(this.selectedPin?.latitude ?? this.dataDoc.latitude), - config_longitude: NumCast(this.selectedPin?.longitude ?? this.dataDoc.longitude), + config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude), + config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude), config_map_zoom: NumCast(this.dataDoc.map_zoom), config_map_type: StrCast(this.dataDoc.map_type), - config_map: StrCast(this.selectedPin?.map) || StrCast(this.dataDoc.map), + config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map), layout_unrendered: true, }); if (anchor) { - anchor.mapPin = this.selectedPin; + anchor.mapPin = existingPin ?? this.selectedPin; if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; /* addAsAnnotation &&*/ this.addDocument(anchor); PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc); @@ -495,6 +499,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.allMapPushpins.map(doc => doc[Highlight]), + () => this.allAnnotations.map(doc => doc[Highlight]), () => { - const allPins = this.allMapPushpins.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin); - allPins.forEach(({ doc, pushpin }) => { + const allConfigPins = this.allAnnotations.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin); + allConfigPins.forEach(({ doc, pushpin }) => { if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) { this.recolorPin(pushpin); this.map_pinHighlighted.delete(pushpin); } }); - allPins.forEach(({ doc, pushpin }) => { + allConfigPins.forEach(({ doc, pushpin }) => { if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) { this.recolorPin(pushpin, 'orange'); this.map_pinHighlighted.set(pushpin, true); @@ -737,30 +742,32 @@ export class MapBox extends ViewBoxAnnotatableComponent {!this._mapReady ? null - : this.allMapPushpins.map(pushpin => ( - - ))} + : this.allAnnotations + .filter(anno => !anno.layout_unrendered) + .map(pushpin => ( + + ))}
{/* boolean; loaded?: (nw: number, nh: number, np: number) => void; setPdfViewer: (view: PDFViewer) => void; - anchorMenuClick?: () => undefined | ((anchor: Doc, summarize?: boolean) => void); + anchorMenuClick?: () => undefined | ((anchor: Doc) => void); crop: (region: Doc | undefined, addCrop?: boolean) => Doc | undefined; } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 7ba4f0e6f..501114157 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1483,14 +1483,14 @@ export namespace Doc { // filters document in a container collection: // all documents with the specified value for the specified key are included/excluded // based on the modifiers :"check", "x", undefined - export function setDocFilter(container: Opt, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) { + export function setDocFilter(container: Opt, key: string, value: any, modifiers: 'removeAll' | 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) { if (!container) return; const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters'; const childFilters = StrListCast(container[filterField]); runInAction(() => { for (let i = 0; i < childFilters.length; i++) { const fields = childFilters[i].split(FilterSep); // split key:value:modifier - if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) { + if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || modifiers === 'removeAll' || (fields[2] === 'match' && modifiers === 'remove'))) { if (fields[2] === modifiers && modifiers && fields[1] === value.toString()) { if (toggle) modifiers = 'remove'; else return; @@ -1502,7 +1502,7 @@ export namespace Doc { } if (!childFilters.length && modifiers === 'match' && value === undefined) { container[filterField] = undefined; - } else if (modifiers !== 'remove') { + } else if (modifiers !== 'remove' && modifiers !== 'removeAll') { !append && (childFilters.length = 0); childFilters.push(key + FilterSep + value + FilterSep + modifiers); container[filterField] = new List(childFilters); diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index e33a17416..8eeb52709 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -1,8 +1,7 @@ -import { makeInterface, createSchema, listSpec } from './Schema'; -import { ScriptField } from './ScriptField'; -import { Doc } from './Doc'; import { DateField } from './DateField'; -import { SchemaHeaderField } from './SchemaHeaderField'; +import { Doc } from './Doc'; +import { createSchema, listSpec, makeInterface } from './Schema'; +import { ScriptField } from './ScriptField'; export const documentSchema = createSchema({ // content properties @@ -26,8 +25,8 @@ export const documentSchema = createSchema({ z: 'number', // z "coordinate" - non-zero specifies the overlay layer of a freeformview zIndex: 'number', // zIndex of a document in a freeform view _layout_scrollTop: 'number', // scroll position of a scrollable document (pdf, text, web) - lat: 'number', - lng: 'number', + latitude: 'number', + longitude: 'number', // appearance properties on the layout '_backgroundGrid-spacing': 'number', // the size of the grid for collection views -- cgit v1.2.3-70-g09d2 From f03492f21046840d51ae59de53e87b769f764a79 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Sun, 27 Aug 2023 12:13:10 -0400 Subject: numerical slices with extra string ($, %, #) show everything --- .../views/nodes/DataVizBox/components/PieChart.tsx | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 200e51240..30a6deea7 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -127,7 +127,7 @@ export class PieChart extends React.Component { : validData.map((d: { [x: string]: any }) => this.byCategory ? d[field] // - : +d[field].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { // drawing the slices var selected = this.selectedData; var arcs = g.selectAll('arc').data(pie(data)).enter().append('g'); - const sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::')); - const possibleDataPointVals = pieDataSet.map((each: { [x: string]: any | { valueOf(): number } }) => { + const possibleDataPointVals: {[x: string]: any}[] = []; + pieDataSet.forEach((each: { [x: string]: any | { valueOf(): number } }) => { + var dataPointVal : {[x: string]: any} = {}; + dataPointVal[percentField] = each[percentField]; + if (descriptionField) dataPointVal[descriptionField] = each[descriptionField]; try { - each[percentField] = Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\ each.split('::')); arcs.append('path') .attr('fill', (d, i) => { var dataPoint; @@ -265,8 +267,7 @@ export class PieChart extends React.Component { var sliceColor; if (dataPoint) { const sliceTitle = dataPoint[this.props.axes[0]]; - const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\ each.split('::')); + const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\ each[0] == accessByName && (sliceColor = each[1])); } return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length]; @@ -301,9 +302,9 @@ export class PieChart extends React.Component { .text(function (d) { var dataPoint; const possibleDataPoints = possibleDataPointVals.filter((pval: any) => pval[percentField] === Number(d.data)); - if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0]; + if (possibleDataPoints.length == 1) dataPoint = pieDataSet[possibleDataPointVals.indexOf(possibleDataPoints[0])]; else { - dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]; + dataPoint = pieDataSet[possibleDataPointVals.indexOf(possibleDataPoints[trackDuplicates[d.data.toString()]])]; trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1; } return dataPoint ? dataPoint[percentField]! + (!descriptionField ? '' : ' - ' + dataPoint[descriptionField])! : ''; @@ -313,7 +314,7 @@ export class PieChart extends React.Component { @action changeSelectedColor = (color: string) => { this.curSliceSelected.attr('fill', color); const sliceTitle = this._currSelected[this.props.axes[0]]; - const sliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\ { @@ -332,7 +333,7 @@ export class PieChart extends React.Component { var curSelectedSliceName = ''; if (this._currSelected) { const sliceTitle = this._currSelected[this.props.axes[0]]; - curSelectedSliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\ { key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : ''; -- cgit v1.2.3-70-g09d2 From a429e628d0053f087270638756bd72c55da2e0d9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 12:16:05 -0400 Subject: removed icloud files --- src/client/util/.ReportManager.scss.icloud | Bin 168 -> 0 bytes src/client/util/.ReportManager.tsx.icloud | Bin 167 -> 0 bytes src/client/views/.Palette.scss.icloud | Bin 160 -> 0 bytes src/client/views/nodes/.QueryBox.scss.icloud | Bin 160 -> 0 bytes src/client/views/nodes/.QueryBox.tsx.icloud | Bin 160 -> 0 bytes src/fields/.ListSpec.ts.icloud | Bin 158 -> 0 bytes src/server/stats/.userLoginStats.csv.icloud | Bin 168 -> 0 bytes 7 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/client/util/.ReportManager.scss.icloud delete mode 100644 src/client/util/.ReportManager.tsx.icloud delete mode 100644 src/client/views/.Palette.scss.icloud delete mode 100644 src/client/views/nodes/.QueryBox.scss.icloud delete mode 100644 src/client/views/nodes/.QueryBox.tsx.icloud delete mode 100644 src/fields/.ListSpec.ts.icloud delete mode 100644 src/server/stats/.userLoginStats.csv.icloud (limited to 'src') diff --git a/src/client/util/.ReportManager.scss.icloud b/src/client/util/.ReportManager.scss.icloud deleted file mode 100644 index f5d34d292..000000000 Binary files a/src/client/util/.ReportManager.scss.icloud and /dev/null differ diff --git a/src/client/util/.ReportManager.tsx.icloud b/src/client/util/.ReportManager.tsx.icloud deleted file mode 100644 index 72924c53a..000000000 Binary files a/src/client/util/.ReportManager.tsx.icloud and /dev/null differ diff --git a/src/client/views/.Palette.scss.icloud b/src/client/views/.Palette.scss.icloud deleted file mode 100644 index 49a2ac2da..000000000 Binary files a/src/client/views/.Palette.scss.icloud and /dev/null differ diff --git a/src/client/views/nodes/.QueryBox.scss.icloud b/src/client/views/nodes/.QueryBox.scss.icloud deleted file mode 100644 index 26ba46a75..000000000 Binary files a/src/client/views/nodes/.QueryBox.scss.icloud and /dev/null differ diff --git a/src/client/views/nodes/.QueryBox.tsx.icloud b/src/client/views/nodes/.QueryBox.tsx.icloud deleted file mode 100644 index 5930b1e0e..000000000 Binary files a/src/client/views/nodes/.QueryBox.tsx.icloud and /dev/null differ diff --git a/src/fields/.ListSpec.ts.icloud b/src/fields/.ListSpec.ts.icloud deleted file mode 100644 index 2fbd1858e..000000000 Binary files a/src/fields/.ListSpec.ts.icloud and /dev/null differ diff --git a/src/server/stats/.userLoginStats.csv.icloud b/src/server/stats/.userLoginStats.csv.icloud deleted file mode 100644 index 4a95297e8..000000000 Binary files a/src/server/stats/.userLoginStats.csv.icloud and /dev/null differ -- cgit v1.2.3-70-g09d2 From a4cd69abc173b77a9920e605533315a28d45b05e Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 12:25:51 -0400 Subject: fixed some colors. reordered property menus. --- src/client/views/PropertiesView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 4a527222c..b6aabb662 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -191,7 +191,7 @@ export class PropertiesView extends React.Component { }); rows.push( -
+
''} SetValue={this.setKeyValue} />
); @@ -1674,11 +1674,11 @@ export class PropertiesView extends React.Component {
{this.editableTitle}
{this.currentType}
- {this.contextsSubMenu} + {this.optionsSubMenu} {this.linksSubMenu} {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : this.linkProperties} {this.inkSubMenu} - {this.optionsSubMenu} + {this.contextsSubMenu} {this.fieldsSubMenu} {isNovice ? null : this.sharingSubMenu} {isNovice ? null : this.filtersSubMenu} -- cgit v1.2.3-70-g09d2 From f259986fec815660af93a3bf08e6f1baec029b85 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 12:37:04 -0400 Subject: made info icon more visible for dark mode --- src/client/views/PropertiesView.scss | 3 +++ src/client/views/PropertiesView.tsx | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index e2e06cde9..bb91ee53d 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -42,6 +42,9 @@ margin-right: 10; float: right; font-size: 20; + path { + fill: white !important; + } } .propertiesView-sharing { diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index b6aabb662..37b61ab41 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1668,7 +1668,7 @@ export class PropertiesView extends React.Component { Properties
window.open('https://brown-dash.github.io/Dash-Documentation//properties/')}> - {' '} + {' '}
-- cgit v1.2.3-70-g09d2 From 5f5bdc03745a05712bf3ccd68cb59c1e814d10f2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 13:14:12 -0400 Subject: added ctrl-a to select all in tableBox --- .../views/nodes/DataVizBox/components/PieChart.tsx | 7 ++- .../views/nodes/DataVizBox/components/TableBox.tsx | 54 ++++++++++++---------- 2 files changed, 32 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 30a6deea7..6ad9e0d7e 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -9,7 +9,6 @@ import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; -import { LinkManager } from '../../../../util/LinkManager'; import { undoable } from '../../../../util/UndoManager'; import { PinProps, PresBox } from '../../trails'; import './Chart.scss'; @@ -244,16 +243,16 @@ export class PieChart extends React.Component { // drawing the slices var selected = this.selectedData; var arcs = g.selectAll('arc').data(pie(data)).enter().append('g'); - const possibleDataPointVals: {[x: string]: any}[] = []; + const possibleDataPointVals: { [x: string]: any }[] = []; pieDataSet.forEach((each: { [x: string]: any | { valueOf(): number } }) => { - var dataPointVal : {[x: string]: any} = {}; + var dataPointVal: { [x: string]: any } = {}; dataPointVal[percentField] = each[percentField]; if (descriptionField) dataPointVal[descriptionField] = each[descriptionField]; try { dataPointVal[percentField] = Number(dataPointVal[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\ each.split('::')); arcs.append('path') .attr('fill', (d, i) => { diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index c416d7764..6688bcedb 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -5,7 +5,7 @@ import { Doc, Field, NumListCast, StrListCast } from '../../../../../fields/Doc' import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast } from '../../../../../fields/Types'; -import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils'; +import { emptyFunction, numberRange, setupMoveUpEvents, Utils } from '../../../../../Utils'; import { DragManager } from '../../../../util/DragManager'; import { LinkManager } from '../../../../util/LinkManager'; import { DocumentView } from '../../DocumentView'; @@ -71,20 +71,18 @@ export class TableBox extends React.Component { render() { if (this._tableData.length > 0) { return ( -
-
-
{ return embedding; }; if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { - DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { - dragComplete: e => { - if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { - e.linkDocument.link_displayLine = true; - e.linkDocument.link_matchEmbeddings = true; - // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; - // e.annoDragData.linkSourceDoc.followLinkZoom = false; - } - }, - }); + DragManager.StartAnchorAnnoDrag( + e.target instanceof HTMLElement ? [e.target] : [], + new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), + downX, + downY, + { + dragComplete: e => { + if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { + e.linkDocument.link_displayLine = true; + e.linkDocument.link_matchEmbeddings = true; + // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; + // e.annoDragData.linkSourceDoc.followLinkZoom = false; + } + }, + } + ); return true; } return false; -- cgit v1.2.3-70-g09d2 From 2df1f976b5f79213299ea7169c9ded8b216dee6e Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 13:16:30 -0400 Subject: from last --- src/client/documents/Documents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index da3cbcb7b..5b68c2d84 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1597,7 +1597,7 @@ export namespace DocUtils { const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) .filter(btnDoc => !btnDoc.hidden) .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) - .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title && doc.title != '') + .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title) .map((dragDoc, i) => ({ description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), event: undoable((args: { x: number; y: number }) => { -- cgit v1.2.3-70-g09d2 From 0332a80329328e39c244623d5090331b83df339c Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 13:31:46 -0400 Subject: minor cleanup of componentDidMount stuff for dataViz --- src/client/documents/Documents.ts | 2 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 8 ++------ src/client/views/nodes/DataVizBox/components/Histogram.tsx | 1 - src/client/views/nodes/DataVizBox/components/LineChart.tsx | 3 +-- src/client/views/nodes/DataVizBox/components/PieChart.tsx | 1 - 5 files changed, 4 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5b68c2d84..7f3309ea8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -689,7 +689,7 @@ export namespace Docs { DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { dataViz_title: '', dataViz: 'table', _layout_fitWidth: true, nativeDimModifiable: true }, + options: { dataViz_title: '', dataViz_line: '', dataViz_pie: '', dataViz_histogram: '', dataViz: 'table', _layout_fitWidth: true, nativeDimModifiable: true }, }, ], [ diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index b14525993..b9db5fe15 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -35,12 +35,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { // all CSV records in the dataset (that aren't an empty row) @computed.struct get records() { - var records = DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href) - var nonEmptyRecords = records?.filter(record => { - var notEmpty = false; - Object.keys(record).forEach(key => { if (record[key]!=undefined && record[key]!="") notEmpty = true}); - return notEmpty}) - return nonEmptyRecords; + var records = DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); + return records?.filter(record => Object.keys(record).some(key => record[key])); } // currently chosen visualization type: line, pie, histogram, table diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 1d8ab8f2a..e67e2bf31 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -106,7 +106,6 @@ export class Histogram extends React.Component { ({ dataSet, w, h }) => dataSet!.length > 0 && this.drawChart(dataSet, w, h), { fireImmediately: true } ); - if (!this.props.layoutDoc.dataViz_histogram) this.props.layoutDoc.dataViz_histogram = ''; }; @action diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 8967f27b8..a69d309dc 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -76,7 +76,7 @@ export class LineChart extends React.Component { // return selected x and y axes // otherwise, use the selection of whatever is linked to us const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox; - const highlitedRowIds = incomingVizBox && incomingVizBox.rootDoc ? NumListCast(incomingVizBox.rootDoc.dataViz_highlitedRows) : []; + const highlitedRowIds = NumListCast(incomingVizBox?.rootDoc?.dataViz_highlitedRows); return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor } @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } { @@ -347,7 +347,6 @@ export class LineChart extends React.Component { } render() { - this.componentDidMount(); var titleAccessor: any = ''; 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]; diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 6ad9e0d7e..4e23a114a 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -94,7 +94,6 @@ export class PieChart extends React.Component { }, { fireImmediately: true } ); - if (!this.props.layoutDoc.dataViz_pie) this.props.layoutDoc.dataViz_pie = ''; }; @action -- cgit v1.2.3-70-g09d2 From 74d9c13e5451a582c44966081886c259a1deb9fd Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 13:41:59 -0400 Subject: update usercolor refs --- src/client/views/MainView.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index c81bc70b8..9ffb804f2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -748,7 +748,7 @@ export class MainView extends React.Component { @computed get leftMenuPanel() { return ( -
+
- +
{this.dockingContent} @@ -880,7 +880,7 @@ export class MainView extends React.Component { @computed get docButtons() { return !Doc.MyDockedBtns ? null : ( -
+
(ele => (ele.scrollTop = ele.scrollLeft = 0))(document.getElementById('root')!)} ref={r => { -- cgit v1.2.3-70-g09d2 From 62826dfca7e2d1785c4d808544023d1d5708d611 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 13:44:36 -0400 Subject: fromlast --- .../views/nodes/DataVizBox/components/TableBox.tsx | 7 +++---- src/fields/Doc.ts | 23 ++++++++++------------ 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 6688bcedb..5045fde3a 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -1,17 +1,16 @@ +import { Button, Type } from 'browndash-components'; import { action, computed, IReactionDisposer, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, Field, NumListCast, StrListCast } from '../../../../../fields/Doc'; +import { Doc, Field, NumListCast } from '../../../../../fields/Doc'; import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast } from '../../../../../fields/Types'; -import { emptyFunction, numberRange, setupMoveUpEvents, Utils } from '../../../../../Utils'; +import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils'; import { DragManager } from '../../../../util/DragManager'; -import { LinkManager } from '../../../../util/LinkManager'; import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; import './Chart.scss'; -import { Button, Type } from 'browndash-components'; interface TableBoxProps { rootDoc: Doc; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index f242ab5be..c8564e5bb 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1461,37 +1461,35 @@ export namespace Doc { prevLayout === 'icon' && (doc.deiconifyLayout = undefined); doc.layout_fieldKey = deiconify || 'layout'; } - export function setDocRangeFilter(container: Opt, key: string, range?: readonly number[], modifiers?: 'remove') { //, modifiers: 'remove' | 'set' + 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) { if (childFiltersByRanges[i] === key) { - console.log("this is key inside childfilters by range " + key) + console.log('this is key inside childfilters by range ' + key); childFiltersByRanges.splice(i, 3); - console.log("this is child filters by range " + childFiltersByRanges) + console.log('this is child filters by range ' + childFiltersByRanges); break; } } if (range !== undefined) { - console.log("in doc.ts in set range filter") + console.log('in doc.ts in set range filter'); childFiltersByRanges.push(key); 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) + 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) + 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]) + console.log('this is child filters by range END' + childFiltersByRanges[0] + ',' + childFiltersByRanges[1] + ',' + childFiltersByRanges[2]); } export const FilterSep = '::'; @@ -1600,7 +1598,6 @@ export namespace Doc { // prettier-ignore export function toIcon(doc?: Doc, isOpen?: boolean) { - console.log(doc!.title, doc!.type) switch (isOpen !== undefined ? DocumentType.COL: StrCast(doc?.type)) { case DocumentType.IMG: return 'image'; case DocumentType.COMPARISON: return 'columns'; -- cgit v1.2.3-70-g09d2 From 966a049a50e02eafd4dc5eb3941c05c5dd12abae Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 13:56:49 -0400 Subject: from laste --- src/client/views/Palette.tsx | 4 ++-- src/client/views/nodes/DataVizBox/components/LineChart.tsx | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 3ad28c418..749eb08a2 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -56,8 +56,8 @@ export default class Palette extends React.Component { styleProvider={returnEmptyString} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} />
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index a69d309dc..3de7a0c4a 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -1,21 +1,19 @@ +import { EditableText, Size } from 'browndash-components'; +import * as d3 from 'd3'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import * as d3 from 'd3'; import { Doc, DocListCast, NumListCast, StrListCast } from '../../../../../fields/Doc'; -import { Id } from '../../../../../fields/FieldSymbols'; import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; import { DocumentManager } from '../../../../util/DocumentManager'; -import { LinkManager } from '../../../../util/LinkManager'; +import { undoable } from '../../../../util/UndoManager'; import { PinProps, PresBox } from '../../trails'; import { DataVizBox } from '../DataVizBox'; import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils'; import './Chart.scss'; -import { EditableText, Size } from 'browndash-components'; -import { undoable } from '../../../../util/UndoManager'; export interface DataPoint { x: number; -- cgit v1.2.3-70-g09d2 From a1aab448616e3330bf53f82a8bd29209850a1d23 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 14:01:55 -0400 Subject: more cleanup --- src/client/util/CurrentUserUtils.ts | 7 +++---- src/client/util/SettingsManager.tsx | 3 --- src/client/views/PropertiesView.scss | 8 ++++---- 3 files changed, 7 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 672f7d99f..ea995e4af 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,7 +1,6 @@ import { observable, reaction, runInAction } from "mobx"; import * as rp from 'request-promise'; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; -import { FieldLoader } from "../../fields/FieldLoader"; import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; @@ -12,7 +11,7 @@ import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; import { SetCachedGroups, SharingPermissions } from "../../fields/util"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; -import { OmitKeys, Utils, addStyleSheetRule } from "../../Utils"; +import { addStyleSheetRule, OmitKeys, Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents"; import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; @@ -20,8 +19,9 @@ import { TreeViewType } from "../views/collections/CollectionTreeView"; import { DashboardView } from "../views/DashboardView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; -import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox"; import { OpenWhere } from "../views/nodes/DocumentView"; +import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox"; +import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; import { OverlayView } from "../views/OverlayView"; import { DragManager, dropActionType } from "./DragManager"; import { MakeTemplate } from "./DropConverter"; @@ -30,7 +30,6 @@ import { LinkManager } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; import { ColorScheme, SettingsManager } from "./SettingsManager"; import { UndoManager } from "./UndoManager"; -import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; interface Button { // DocumentOptions fields a button can set diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 8133e9eff..4814c3644 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -17,9 +17,6 @@ import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; import { undoBatch } from './UndoManager'; -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; export enum ColorScheme { Dark = 'Dark', diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 63b9b53c2..b116a622c 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -267,10 +267,10 @@ } } -.propertiesView-presentationTrails { - //border-bottom: 1px solid black; - //padding: 8.5px; -} +//.propertiesView-presentationTrails { +//border-bottom: 1px solid black; +//padding: 8.5px; +//} .inking-button { display: flex; -- cgit v1.2.3-70-g09d2 From 8a43ea9c37fe9ae671391bc264e3760cd90d53c5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 14:21:10 -0400 Subject: working cleanup version --- src/client/util/GroupManager.tsx | 14 +++++++------- src/client/util/SettingsManager.tsx | 15 ++------------- src/client/util/SharingManager.tsx | 1 - src/client/views/MainView.tsx | 17 ++++++++--------- src/client/views/MainViewModal.tsx | 9 ++++----- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 1 - 6 files changed, 21 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index f35844020..c79894032 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -1,22 +1,22 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Button, IconButton, Size, Type } from 'browndash-components'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import Select from 'react-select'; import * as RequestPromise from 'request-promise'; -import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc'; -import { StrCast, Cast } from '../../fields/Types'; +import { DateField } from '../../fields/DateField'; +import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { Id } from '../../fields/FieldSymbols'; +import { listSpec } from '../../fields/Schema'; +import { Cast, StrCast } from '../../fields/Types'; import { Utils } from '../../Utils'; import { MainViewModal } from '../views/MainViewModal'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import './GroupManager.scss'; import { GroupMemberView } from './GroupMemberView'; -import { SharingManager, User } from './SharingManager'; -import { listSpec } from '../../fields/Schema'; -import { DateField } from '../../fields/DateField'; -import { Id } from '../../fields/FieldSymbols'; -import { Button, IconButton, Size, Type } from 'browndash-components'; import { SettingsManager } from './SettingsManager'; +import { SharingManager, User } from './SharingManager'; /** * Interface for options for the react-select component diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 4814c3644..bb370e1a4 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -48,13 +48,6 @@ export class SettingsManager extends React.Component<{}> { @observable public static propertiesWidth: number = 0; @observable public static headerBarHeight: number = 0; - @computed get backgroundColor() { - return Doc.UserDoc().activeCollectionBackground; - } - @computed get userTheme() { - return Doc.UserDoc().userTheme; - } - constructor(props: {}) { super(props); SettingsManager.Instance = this; @@ -94,12 +87,8 @@ export class SettingsManager extends React.Component<{}> { Doc.UserDoc().userBackgroundColor = color; addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${color} !important` }); }); - @undoBatch switchUserColor = action((color: string) => { - Doc.UserDoc().userColor = color; - }); - @undoBatch switchUserVariantColor = action((color: string) => { - Doc.UserDoc().userVariantColor = color; - }); + @undoBatch switchUserColor = action((color: string) => (Doc.UserDoc().userColor = color)); + @undoBatch switchUserVariantColor = action((color: string) => (Doc.UserDoc().userVariantColor = color)); @undoBatch playgroundModeToggle = action(() => { this.playgroundMode = !this.playgroundMode; if (this.playgroundMode) { diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 6171c01d7..81ddeb9e3 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -22,7 +22,6 @@ import { SearchBox } from '../views/search/SearchBox'; import { DocumentManager } from './DocumentManager'; import { GroupManager, UserOptions } from './GroupManager'; import { GroupMemberView } from './GroupMemberView'; -import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; import { SettingsManager } from './SettingsManager'; import './SharingManager.scss'; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9ffb804f2..6a743a5e6 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -43,7 +43,6 @@ import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import { GestureOverlay } from './GestureOverlay'; import { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } from './global/globalCssVariables.scss'; -import { Colors } from './global/globalEnums'; import { KeyManager } from './GlobalKeyHandler'; import { InkTranscription } from './InkTranscription'; import { LightboxView } from './LightboxView'; @@ -748,7 +747,7 @@ export class MainView extends React.Component { @computed get leftMenuPanel() { return ( -
+
- +
{this.dockingContent} {this._hideUI ? null : ( -
- +
+
)}
@@ -880,7 +879,7 @@ export class MainView extends React.Component { @computed get docButtons() { return !Doc.MyDockedBtns ? null : ( -
+
(ele => (ele.scrollTop = ele.scrollLeft = 0))(document.getElementById('root')!)} ref={r => { diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index 42df99864..d0a79eb70 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,10 +1,9 @@ -import * as React from 'react'; -import './MainViewModal.scss'; +import { isDark } from 'browndash-components'; import { observer } from 'mobx-react'; +import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { StrCast } from '../../fields/Types'; -import { isDark } from 'browndash-components'; -import { Colors } from './global/globalEnums'; +import './MainViewModal.scss'; export interface MainViewOverlayProps { isDisplayed: boolean; @@ -45,7 +44,7 @@ export class MainViewModal extends React.Component { className="overlay" onClick={this.props?.closeOnExternalClick} style={{ - backgroundColor: isDark(StrCast(Doc.UserDoc().userColor)) ? "#DFDFDF30" : "#32323230", + backgroundColor: isDark(StrCast(Doc.UserDoc().userColor)) ? '#DFDFDF30' : '#32323230', ...(p.overlayStyle || {}), }} /> diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index d132707fa..94650cc88 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -7,7 +7,6 @@ import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { colorMapping } from '../../../../server/DashSession/Session/utilities/session_config'; import { Utils } from '../../../../Utils'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; -- cgit v1.2.3-70-g09d2 From ae0765b2836f321c7240b68004ead5ddb18ce680 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Sun, 27 Aug 2023 14:22:35 -0400 Subject: one column numerical pie histogram vs weighted slices checkbox --- src/client/views/nodes/DataVizBox/components/Chart.scss | 12 ++++++++++++ .../views/nodes/DataVizBox/components/PieChart.tsx | 16 +++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss index 9359919c6..50dfe7f05 100644 --- a/src/client/views/nodes/DataVizBox/components/Chart.scss +++ b/src/client/views/nodes/DataVizBox/components/Chart.scss @@ -17,6 +17,18 @@ margin-top: -10px; margin-bottom: -10px; } + .asHistogram-checkBox { + // display: flex; + // flex-direction: row; + align-items: left; + align-self: left; + align-content: left; + justify-content: flex-end; + float: left; + left: 0; + position: relative; + margin-bottom: -35px; + } .selected-data{ align-items: center; text-align: center; diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 4e23a114a..4bd476bf3 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -12,6 +12,7 @@ import { Docs } from '../../../../documents/Documents'; import { undoable } from '../../../../util/UndoManager'; import { PinProps, PresBox } from '../../trails'; import './Chart.scss'; +import { Checkbox } from '@material-ui/core'; export interface PieChartProps { rootDoc: Doc; @@ -51,7 +52,8 @@ export class PieChart extends React.Component { // organized by specified number percentages/ratios if one column is selected and it contains numbers // otherwise, assume data is organized by categories @computed get byCategory() { - return !/\d/.test(this.props.records[0][this.props.axes[0]]); + if (this.props.layoutDoc.dataViz_pie_asHistogram==undefined) this.props.layoutDoc.dataViz_pie_asHistogram = false; + return !/\d/.test(this.props.records[0][this.props.axes[0]]) || this.props.layoutDoc.dataViz_pie_asHistogram; } // filters all data to just display selected data if brushed (created from an incoming link) @computed get _pieChartData() { @@ -321,6 +323,11 @@ export class PieChart extends React.Component { sliceColors.push(StrCast(sliceName + '::' + color)); }; + @action changeHistogramCheckBox = () => { + this.props.layoutDoc.dataViz_pie_asHistogram = !this.props.layoutDoc.dataViz_pie_asHistogram + this.drawChart(this._pieChartData, this.width, this.height) + } + render() { var titleAccessor: any = ''; if (this.props.axes.length == 2) titleAccessor = 'dataViz_pie_title' + this.props.axes[0] + '-' + this.props.axes[1]; @@ -360,6 +367,13 @@ export class PieChart extends React.Component { fillWidth />
+ { (this.props.axes.length === 1 && /\d/.test(this.props.records[0][this.props.axes[0]]))? +
+ + Organize data as histogram +
+ : null + }
{selected != 'none' ? (
-- cgit v1.2.3-70-g09d2 From 52a2aaf9bc241ee39dce7510958fb192860a876c Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 14:23:30 -0400 Subject: unused higflyout refs --- src/client/util/reportManager/ReportManager.tsx | 3 --- src/client/views/PropertiesButtons.tsx | 3 --- src/client/views/PropertiesView.tsx | 3 --- src/client/views/nodes/DocumentLinksButton.tsx | 4 ---- 4 files changed, 13 deletions(-) (limited to 'src') diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx index 7aad0f2b1..3cd2d47a0 100644 --- a/src/client/util/reportManager/ReportManager.tsx +++ b/src/client/util/reportManager/ReportManager.tsx @@ -20,9 +20,6 @@ import { Filter, FormInput, FormTextArea, IssueCard, IssueView, Tag } from './Re import { StrCast } from '../../../fields/Types'; import { MdRefresh } from 'react-icons/md'; import { SettingsManager } from '../SettingsManager'; -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; /** * Class for reporting and viewing Github issues within the app. diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 8cae34d7d..42db0b9be 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -32,9 +32,6 @@ import { TfiBarChart } from 'react-icons/tfi'; import { CiGrid31 } from 'react-icons/ci'; import { RxWidth } from 'react-icons/rx'; import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components'; -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; enum UtilityButtonState { Default, diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 9df04c862..4fe6847c3 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -40,9 +40,6 @@ import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView import { KeyValueBox } from './nodes/KeyValueBox'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; import { SettingsManager } from '../util/SettingsManager'; -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; const _global = (window /* browser */ || global) /* node */ as any; interface PropertiesViewProps { diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 7723a088d..4db0bf5fa 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -18,10 +18,6 @@ import React = require('react'); import _ = require('lodash'); import { PinProps } from './trails'; -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; - interface DocumentLinksButtonProps { View: DocumentView; Bottom?: boolean; -- cgit v1.2.3-70-g09d2 From efc6e14ea9019ee16813f69aa195119638921523 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 14:28:57 -0400 Subject: from last --- src/fields/Doc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c8564e5bb..09aac03ea 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1603,7 +1603,7 @@ export namespace Doc { case DocumentType.COMPARISON: return 'columns'; case DocumentType.RTF: return 'sticky-note'; case DocumentType.COL: - const folder: IconProp = isOpen === true ? 'folder-open' : isOpen === false ? 'folder' : doc!.title=='Untitled Collection'? 'object-group': 'chalkboard'; + const folder: IconProp = isOpen === true ? 'folder-open' : isOpen === false ? 'folder' : doc?.title==='Untitled Collection'? 'object-group': 'chalkboard'; const chevron: IconProp = isOpen === true ? 'chevron-down' : isOpen === false ? 'chevron-right' : 'question'; return !doc?.isFolder ? folder : chevron; case DocumentType.WEB: return 'globe-asia'; -- cgit v1.2.3-70-g09d2 From 2faf9b1e8d8f0fbaecb767f552b9d16a6b49a8de Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 14:33:39 -0400 Subject: from last --- src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index ebb4ba5a1..7a22d4871 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -22,9 +22,6 @@ import { EditableView } from '../EditableView'; import './CollectionStackingView.scss'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { Id } from '../../../fields/FieldSymbols'; -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; // So this is how we are storing a column interface CSVFieldColumnProps { -- cgit v1.2.3-70-g09d2 From 9405a97acabb70ac671029f61e825ba00a8dc3ce Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 14:37:42 -0400 Subject: breaking include cycles --- src/client/util/SelectionManager.ts | 31 +++++++++++++++++++++++++++++-- src/fields/Doc.ts | 30 +++--------------------------- 2 files changed, 32 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 4be9448b3..dbf33fcf5 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,11 +1,14 @@ import { action, observable, ObservableMap } from 'mobx'; import { computedFn } from 'mobx-utils'; import { Doc, Opt } from '../../fields/Doc'; -import { DocCast } from '../../fields/Types'; -import { CollectionViewType } from '../documents/DocumentTypes'; +import { List } from '../../fields/List'; +import { listSpec } from '../../fields/Schema'; +import { Cast, DocCast } from '../../fields/Types'; +import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { DocumentView } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; import { ScriptingGlobals } from './ScriptingGlobals'; +import { UndoManager } from './UndoManager'; export namespace SelectionManager { class Manager { @@ -124,3 +127,27 @@ ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, exp ScriptingGlobals.add(function deselectAll() { SelectionManager.DeselectAll(); }); +ScriptingGlobals.add(function undo() { + SelectionManager.DeselectAll(); + return UndoManager.Undo(); +}); + +export function ShowUndoStack() { + SelectionManager.DeselectAll(); + var buffer = ''; + UndoManager.undoStack.forEach((batch, i) => { + buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n'; + ///batch.forEach(undo => (buffer += ' ' + undo.prop + '\n')); + }); + alert(buffer); +} +ScriptingGlobals.add(function redo() { + SelectionManager.DeselectAll(); + return UndoManager.Redo(); +}); +ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { + const docs = SelectionManager.Views() + .map(dv => dv.props.Document) + .filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); + return docs.length ? new List(docs) : prevValue; +}); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 09aac03ea..4ad38e7fc 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -7,9 +7,8 @@ import { DocServer } from '../client/DocServer'; import { DocumentType } from '../client/documents/DocumentTypes'; import { LinkManager } from '../client/util/LinkManager'; import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals'; -import { SelectionManager } from '../client/util/SelectionManager'; import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper'; -import { undoable, UndoManager } from '../client/util/UndoManager'; +import { undoable } from '../client/util/UndoManager'; import { decycle } from '../decycler/decycler'; import * as JSZipUtils from '../JSZipUtils'; import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils'; @@ -50,7 +49,7 @@ import { listSpec } from './Schema'; import { ComputedField, ScriptField } from './ScriptField'; import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types'; import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField'; -import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, containedFieldChangedHandler } from './util'; +import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions } from './util'; import JSZip = require('jszip'); export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -1830,24 +1829,6 @@ ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); -ScriptingGlobals.add(function undo() { - SelectionManager.DeselectAll(); - return UndoManager.Undo(); -}); - -export function ShowUndoStack() { - SelectionManager.DeselectAll(); - var buffer = ''; - UndoManager.undoStack.forEach((batch, i) => { - buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n'; - ///batch.forEach(undo => (buffer += ' ' + undo.prop + '\n')); - }); - alert(buffer); -} -ScriptingGlobals.add(function redo() { - SelectionManager.DeselectAll(); - return UndoManager.Redo(); -}); ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a document id in a script"); return 'invalid'; @@ -1862,12 +1843,7 @@ ScriptingGlobals.add(function activePresentationItem() { const curPres = Doc.ActivePresentation; return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)]; }); -ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { - const docs = SelectionManager.Views() - .map(dv => dv.props.Document) - .filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); - return docs.length ? new List(docs) : prevValue; -}); + ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: any, modifiers: 'match' | 'check' | 'x' | 'remove') { Doc.setDocFilter(container, key, value, modifiers); }); -- cgit v1.2.3-70-g09d2 From 9d6c7f8100de3a952d20ad41ab20872737cb909e Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 20:25:55 -0400 Subject: lots of cleanup to streamline import orderings (ie packages should not mutually import each other directly or via a chain). change raiseWhenDragged to be keepZWhenDragged and got rid of system wide default. --- src/Utils.ts | 2 +- src/client/DocServer.ts | 15 ++- src/client/apis/youtube/YoutubeBox.tsx | 6 +- src/client/documents/Documents.ts | 10 +- src/client/util/CurrentUserUtils.ts | 11 +- src/client/util/DictationManager.ts | 2 +- src/client/util/DocumentManager.ts | 22 +++- src/client/util/DragManager.ts | 21 +--- src/client/util/DropConverter.ts | 14 +-- src/client/util/GroupManager.tsx | 4 +- src/client/util/GroupMemberView.tsx | 2 +- src/client/util/SearchUtil.ts | 104 ++++++++++++++- src/client/util/SettingsManager.tsx | 89 ++++++------- src/client/util/SharingManager.tsx | 10 +- src/client/util/reportManager/ReportManager.tsx | 4 +- src/client/views/ContextMenuItem.tsx | 6 +- src/client/views/DocumentDecorations.tsx | 11 +- src/client/views/EditableView.tsx | 1 - src/client/views/FilterPanel.tsx | 23 ++-- src/client/views/LightboxView.tsx | 11 +- src/client/views/Main.tsx | 6 +- src/client/views/MainView.tsx | 38 +++--- src/client/views/PropertiesView.tsx | 20 +-- src/client/views/SidebarAnnos.tsx | 4 +- src/client/views/StyleProvider.tsx | 15 ++- src/client/views/UndoStack.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 4 +- src/client/views/collections/CollectionMenu.tsx | 2 +- .../collections/CollectionStackedTimeline.tsx | 4 +- src/client/views/collections/TabDocView.tsx | 13 +- src/client/views/collections/TreeSort.ts | 6 + src/client/views/collections/TreeView.tsx | 11 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/linking/LinkPopup.tsx | 3 +- .../views/newlightbox/ButtonMenu/ButtonMenu.tsx | 19 ++- src/client/views/newlightbox/NewLightboxView.tsx | 139 ++++++++++----------- src/client/views/nodes/DocumentView.tsx | 16 ++- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 17 +-- src/client/views/nodes/LinkDocPreview.tsx | 6 +- src/client/views/nodes/LoadingBox.tsx | 6 +- src/client/views/nodes/ScreenshotBox.tsx | 6 +- src/client/views/nodes/VideoBox.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 24 ++-- .../views/nodes/generativeFill/GenerativeFill.tsx | 33 +++-- src/client/views/nodes/trails/PresBox.tsx | 4 +- src/client/views/pdf/AnchorMenu.tsx | 15 +-- src/client/views/search/IconBar.tsx | 28 +++-- src/client/views/search/SearchBox.tsx | 110 +--------------- src/client/views/topbar/TopBar.tsx | 16 ++- src/client/views/webcam/DashWebRTCVideo.tsx | 6 +- src/fields/Doc.ts | 26 +++- src/fields/RichTextUtils.ts | 10 +- 52 files changed, 492 insertions(+), 494 deletions(-) create mode 100644 src/client/views/collections/TreeSort.ts (limited to 'src') diff --git a/src/Utils.ts b/src/Utils.ts index 7f83ab8f5..9a94694a2 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -3,10 +3,10 @@ import v5 = require('uuid/v5'); import { ColorState } from 'react-color'; import * as rp from 'request-promise'; import { Socket } from 'socket.io'; +import { DocumentType } from './client/documents/DocumentTypes'; import { Colors } from './client/views/global/globalEnums'; import { Message } from './server/Message'; import Color = require('color'); -import { DocumentType } from './client/documents/DocumentTypes'; export namespace Utils { export let CLICK_TIME = 300; diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 53c7b857a..5fdea131b 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,6 +1,5 @@ import { runInAction } from 'mobx'; import * as rp from 'request-promise'; -import * as io from 'socket.io-client'; import { Doc, DocListCast, Opt } from '../fields/Doc'; import { UpdatingFromServer } from '../fields/DocSymbols'; import { FieldLoader } from '../fields/FieldLoader'; @@ -8,13 +7,13 @@ import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols'; import { ObjectField } from '../fields/ObjectField'; import { RefField } from '../fields/RefField'; import { DocCast, StrCast } from '../fields/Types'; -import MobileInkOverlay from '../mobile/MobileInkOverlay'; +//import MobileInkOverlay from '../mobile/MobileInkOverlay'; import { emptyFunction, Utils } from '../Utils'; import { GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message'; import { DocumentType } from './documents/DocumentTypes'; import { LinkManager } from './util/LinkManager'; import { SerializationHelper } from './util/SerializationHelper'; -import { GestureOverlay } from './views/GestureOverlay'; +//import { GestureOverlay } from './views/GestureOverlay'; /** * This class encapsulates the transfer and cross-client synchronization of @@ -189,17 +188,17 @@ export namespace DocServer { // mobile ink overlay socket events to communicate between mobile view and desktop view _socket.addEventListener('receiveGesturePoints', (content: GestureContent) => { - MobileInkOverlay.Instance.drawStroke(content); + // MobileInkOverlay.Instance.drawStroke(content); }); _socket.addEventListener('receiveOverlayTrigger', (content: MobileInkOverlayContent) => { - GestureOverlay.Instance.enableMobileInkOverlay(content); - MobileInkOverlay.Instance.initMobileInkOverlay(content); + //GestureOverlay.Instance.enableMobileInkOverlay(content); + // MobileInkOverlay.Instance.initMobileInkOverlay(content); }); _socket.addEventListener('receiveUpdateOverlayPosition', (content: UpdateMobileInkOverlayPositionContent) => { - MobileInkOverlay.Instance.updatePosition(content); + // MobileInkOverlay.Instance.updatePosition(content); }); _socket.addEventListener('receiveMobileDocumentUpload', (content: MobileDocumentUploadContent) => { - MobileInkOverlay.Instance.uploadDocument(content); + // MobileInkOverlay.Instance.uploadDocument(content); }); } diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 05879a247..2da9927c0 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -6,7 +6,7 @@ import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { Utils } from '../../../Utils'; import { DocServer } from '../../DocServer'; import { Docs } from '../../documents/Documents'; -import { DocumentDecorations } from '../../views/DocumentDecorations'; +import { DocumentView } from '../../views/nodes/DocumentView'; import { FieldView, FieldViewProps } from '../../views/nodes/FieldView'; import '../../views/nodes/WebBox.scss'; import './YoutubeBox.scss'; @@ -355,9 +355,9 @@ export class YoutubeBox extends React.Component {
); - const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; + const frozen = !this.props.isSelected() || DocumentView.Interacting; - const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? '-interactive' : ''); + const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentView.Interacting ? '-interactive' : ''); return ( <>
{content}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1186446e1..919958b24 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -19,7 +19,6 @@ import { aggregateBounds, OmitKeys, Utils } from '../../Utils'; import { YoutubeBox } from '../apis/youtube/YoutubeBox'; import { DocServer } from '../DocServer'; import { Networking } from '../Network'; -import { DocumentManager } from '../util/DocumentManager'; import { DragManager, dropActionType } from '../util/DragManager'; import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox'; import { FollowLinkScript } from '../util/LinkFollower'; @@ -34,12 +33,12 @@ import { ContextMenuProps } from '../views/ContextMenuItem'; import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss'; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke'; import { AudioBox } from '../views/nodes/AudioBox'; -import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { ColorBox } from '../views/nodes/ColorBox'; import { ComparisonBox } from '../views/nodes/ComparisonBox'; import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox'; import { EquationBox } from '../views/nodes/EquationBox'; import { FieldViewProps } from '../views/nodes/FieldView'; +import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox'; import { ImageBox } from '../views/nodes/ImageBox'; @@ -394,7 +393,7 @@ export class DocumentOptions { onPointerUp?: ScriptField; _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.'); + _keepZWhenDragged?: BOOLt = new BoolInfo('whether a document should keep its z-order when dragged.'); childDragAction?: DROPt = new DAInfo('what should happen to the child documents when they are dragged from the collection'); dropConverter?: ScriptField; // script to run when documents are dropped on this Document. dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); @@ -1371,7 +1370,7 @@ export namespace DocUtils { export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { - broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1)); + broadcastEvent && runInAction(() => (Doc.RecordingEvent = Doc.RecordingEvent + 1)); return DocUtils.ActiveRecordings.map(audio => { const sourceDoc = getSourceDoc(); return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); @@ -1380,7 +1379,6 @@ export namespace DocUtils { export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) { if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; - const sv = DocumentManager.Instance.getDocumentView(source); if (target.doc === Doc.UserDoc()) return undefined; const makeLink = action((linkDoc: Doc, showPopup?: number[]) => { @@ -1840,8 +1838,6 @@ export namespace DocUtils { } if (overwriteDoc) { Doc.removeCurrentlyLoading(overwriteDoc); - // loading doc icons are just labels. so any icon views of loading docs need to be replaced with the proper icon view. - DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())); } generatedDocuments.push(doc); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index ea995e4af..d52e389d6 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -18,7 +18,6 @@ import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; import { TreeViewType } from "../views/collections/CollectionTreeView"; import { DashboardView } from "../views/DashboardView"; import { Colors } from "../views/global/globalEnums"; -import { MainView } from "../views/MainView"; import { OpenWhere } from "../views/nodes/DocumentView"; import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox"; import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; @@ -621,9 +620,9 @@ export class CurrentUserUtils { static freeTools(): Button[] { return [ - { title: "Bottom", icon: "arrows-down-to-line",toolTip: "Make doc topmost", btnType: ButtonType.ClickButton, expertMode: false, funcs: {}, scripts: { onClick: 'sendToBack()'}}, // Only when floating document is selected in freeform - { title: "Top", icon: "arrows-up-to-line", toolTip: "Make doc bottommost", btnType: ButtonType.ClickButton, expertMode: false, funcs: {}, scripts: { onClick: 'bringToFront()'}}, // Only when floating document is selected in freeform - { title: "Z order", icon: "z", toolTip: "Bring Forward on Drag (double click to set for all)",waitForDoubleClickToClick:true, btnType: ButtonType.ToggleButton, expertMode: false, funcs: {}, scripts: { onClick: 'toggleRaiseOnDrag(false, _readOnly_)', onDoubleClick:`{ return toggleRaiseOnDrag(true, _readOnly_)`}}, // Only when floating document is selected in freeform + { title: "Bottom", icon: "arrows-down-to-line",toolTip: "Make doc topmost", btnType: ButtonType.ClickButton, expertMode: false, funcs: {}, scripts: { onClick: 'sendToBack()'}}, // Only when floating document is selected in freeform + { title: "Top", icon: "arrows-up-to-line", toolTip: "Make doc bottommost", btnType: ButtonType.ClickButton, expertMode: false, funcs: {}, scripts: { onClick: 'bringToFront()'}}, // Only when floating document is selected in freeform + { title: "Z order", icon: "z", toolTip: "Keep Z order on Drag", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {}, scripts: { onClick: '{ return toggleRaiseOnDrag(_readOnly_);}'}}, // Only when floating document is selected in freeform ] } static viewTools(): Button[] { @@ -841,7 +840,6 @@ export class CurrentUserUtils { doc.isSystem ?? (doc.isSystem = true); doc.title ?? (doc.title = Doc.CurrentUserEmail); Doc.noviceMode ?? (Doc.noviceMode = true); - doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true); doc._showLabel ?? (doc._showLabel = true); doc.textAlign ?? (doc.textAlign = "left"); doc.activeTool = InkTool.None; @@ -996,8 +994,5 @@ export class CurrentUserUtils { ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs"); ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode"); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); -ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called"); -ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); -ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 717473aa1..0fd7e840c 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -11,7 +11,7 @@ import { Utils } from '../../Utils'; import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; import { DictationOverlay } from '../views/DictationOverlay'; -import { DocumentView, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView'; +import { DocumentView, OpenWhere } from '../views/nodes/DocumentView'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 5b627c2f3..c2827dac7 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,4 +1,4 @@ -import { action, computed, observable, ObservableSet } from 'mobx'; +import { action, computed, observable, ObservableSet, observe, reaction } from 'mobx'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { AclAdmin, AclEdit, Animation } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; @@ -23,7 +23,6 @@ export class DocumentManager { //global holds all of the nodes (regardless of which collection they're in) @observable _documentViews = new Set(); @observable public LinkAnchorBoxViews: DocumentView[] = []; - @observable public RecordingEvent = 0; @observable public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = []; @computed public get DocumentViews() { return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox) && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(view.docViewPath))); @@ -41,7 +40,22 @@ export class DocumentManager { } //private constructor so no other class can create a nodemanager - private constructor() {} + private constructor() { + if (!Doc.CurrentlyLoading) Doc.CurrentlyLoading = []; + observe(Doc.CurrentlyLoading, change => { + // watch CurrentlyLoading-- when something is loaded, it's removed from the list and we have to update its icon if it were iconified since LoadingBox icons are different than the media they become + switch (change.type as any) { + case 'update': + break; + case 'remove': + // DocumentManager.Instance.getAllDocumentViews(change as any).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())); + break; + case 'splice': + (change as any).removed.forEach((doc: Doc) => DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify()))); + break; + } + }); + } private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = []; public AddViewRenderedCb = (doc: Opt, func: (dv: DocumentView) => any) => { @@ -310,7 +324,7 @@ export class DocumentManager { if (viewSpec && docView) { if (docView.ComponentView instanceof FormattedTextBox) docView.ComponentView?.focus(viewSpec, options); PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500); - Doc.linkFollowHighlight(viewSpec ? [docView.rootDoc, viewSpec]: docView.rootDoc, undefined, options.effect); + Doc.linkFollowHighlight(viewSpec ? [docView.rootDoc, viewSpec] : docView.rootDoc, undefined, options.effect); if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc); if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden; if (options.effect) docView.rootDoc[Animation] = options.effect; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 489c9df4a..05da5ebed 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -61,12 +61,6 @@ export namespace DragManager { export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void>; export let CompleteWindowDrag: Opt<(aborted: boolean) => void>; - export function GetRaiseWhenDragged() { - return BoolCast(Doc.UserDoc()._raiseWhenDragged); - } - export function SetRaiseWhenDragged(val: boolean) { - Doc.UserDoc()._raiseWhenDragged = val; - } export function Root() { const root = document.getElementById('root'); if (!root) { @@ -605,18 +599,9 @@ export namespace DragManager { } } -ScriptingGlobals.add(function toggleRaiseOnDrag(forAllDocs: boolean, readOnly?: boolean) { +ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) { if (readOnly) { - if (SelectionManager.Views().length) - return SelectionManager.Views().some(dv => dv.rootDoc.raiseWhenDragged) - ? Colors.MEDIUM_BLUE - : SelectionManager.Views().some(dv => dv.rootDoc.raiseWhenDragged === false) - ? 'transparent' - : DragManager.GetRaiseWhenDragged() - ? Colors.MEDIUM_BLUE_ALT - : Colors.LIGHT_BLUE; - return DragManager.GetRaiseWhenDragged() ? Colors.MEDIUM_BLUE_ALT : 'transparent'; + return SelectionManager.Views().some(dv => dv.rootDoc.keepZWhenDragged); } - if (!forAllDocs) SelectionManager.Views().map(dv => (dv.rootDoc.raiseWhenDragged ? (dv.rootDoc.raiseWhenDragged = undefined) : dv.rootDoc.raiseWhenDragged === false ? (dv.rootDoc.raiseWhenDragged = true) : (dv.rootDoc.raiseWhenDragged = false))); - else DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged()); + SelectionManager.Views().map(dv => (dv.rootDoc.keepZWhenDragged = !dv.rootDoc.keepZWhenDragged)); }); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index f235be192..dbdf580cd 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -1,15 +1,15 @@ -import { DragManager } from './DragManager'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { DocumentType } from '../documents/DocumentTypes'; import { ObjectField } from '../../fields/ObjectField'; -import { StrCast, Cast } from '../../fields/Types'; -import { Docs } from '../documents/Documents'; -import { ScriptField, ComputedField } from '../../fields/ScriptField'; import { RichTextField } from '../../fields/RichTextField'; -import { ImageField } from '../../fields/URLField'; -import { ScriptingGlobals } from './ScriptingGlobals'; import { listSpec } from '../../fields/Schema'; +import { ScriptField } from '../../fields/ScriptField'; +import { Cast, StrCast } from '../../fields/Types'; +import { ImageField } from '../../fields/URLField'; +import { Docs } from '../documents/Documents'; +import { DocumentType } from '../documents/DocumentTypes'; import { ButtonType } from '../views/nodes/FontIconBox/FontIconBox'; +import { DragManager } from './DragManager'; +import { ScriptingGlobals } from './ScriptingGlobals'; export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt = undefined, templateField: string = '') { if (templateField) Doc.GetProto(doc).title = templateField; /// the title determines which field is being templated diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index c79894032..8973306bf 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -282,7 +282,7 @@ export class GroupManager extends React.Component<{}> { */ private get groupCreationModal() { const contents = ( -
+

New Group @@ -367,7 +367,7 @@ export class GroupManager extends React.Component<{}> { const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups; return ( -

+
{this.groupCreationModal} {this.currentGroup ? (this.currentGroup = undefined))} /> : null}
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 535d8ccc2..7de0f336f 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -29,7 +29,7 @@ export class GroupMemberView extends React.Component { const hasEditAccess = GroupManager.Instance.hasEditAccess(this.props.group); return !this.props.group ? null : ( -
+
, query: string) { + const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; + const blockedKeys = [ + 'x', + 'y', + 'proto', + 'width', + 'layout_autoHeight', + 'acl-Override', + 'acl-Guest', + 'embedContainer', + 'zIndex', + 'height', + 'text_scrollHeight', + 'text_height', + 'cloneFieldFilter', + 'isDataDoc', + 'text_annotations', + 'dragFactory_count', + 'text_noTemplate', + 'proto_embeddings', + 'isSystem', + 'layout_fieldKey', + 'isBaseProto', + 'xMargin', + 'yMargin', + 'links', + 'layout', + 'layout_keyValue', + 'layout_fitWidth', + 'type_collection', + 'title_custom', + 'freeform_panX', + 'freeform_panY', + 'freeform_scale', + ]; + query = query.toLowerCase(); + + const results = new Map(); + if (rootDoc) { + const docs = DocListCast(rootDoc[Doc.LayoutFieldKey(rootDoc)]); + const docIDs: String[] = []; + SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => { + const dtype = StrCast(doc.type) as DocumentType; + if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) { + const hlights = new Set(); + SearchUtil.documentKeys(doc).forEach( + key => + Field.toString(doc[key] as Field) + .toLowerCase() + .includes(query) && hlights.add(key) + ); + blockedKeys.forEach(key => hlights.delete(key)); + + if (Array.from(hlights.keys()).length > 0) { + results.set(doc, Array.from(hlights.keys())); + } + } + docIDs.push(doc[Id]); + }); + } + return results; + } + /** + * @param {Doc} doc - doc for which keys are returned + * + * This method returns a list of a document doc's keys. + */ + export function documentKeys(doc: Doc) { + const keys: { [key: string]: boolean } = {}; + Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false))); + return Array.from(Object.keys(keys)); + } + + /** + * @param {Doc[]} docs - docs to be searched through recursively + * @param {number, Doc => void} func - function to be called on each doc + * + * This method iterates through an array of docs and all docs within those docs, calling + * the function func on each doc. + */ + export function foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) { + let newarray: Doc[] = []; + var depth = 0; + const visited: Doc[] = []; + while (docs.length > 0) { + newarray = []; + docs.filter(d => d && !visited.includes(d)).forEach(d => { + visited.push(d); + const fieldKey = Doc.LayoutFieldKey(d); + const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView'); + const data = d[annos ? fieldKey + '_annotations' : fieldKey]; + data && newarray.push(...DocListCast(data)); + const sidebar = d[fieldKey + '_sidebar']; + sidebar && newarray.push(...DocListCast(sidebar)); + func(depth, d); + }); + docs = newarray; + depth++; + } + } export interface IdSearchResult { ids: string[]; lines: string[][]; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index bb370e1a4..720badd40 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -13,7 +13,6 @@ import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager import { DocServer } from '../DocServer'; import { Networking } from '../Network'; import { MainViewModal } from '../views/MainViewModal'; -import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; import { undoBatch } from './UndoManager'; @@ -67,15 +66,15 @@ export class SettingsManager extends React.Component<{}> { } }; - @computed get userColor() { + @computed public static get userColor() { return StrCast(Doc.UserDoc().userColor); } - @computed get userVariantColor() { + @computed public static get userVariantColor() { return StrCast(Doc.UserDoc().userVariantColor); } - @computed get userBackgroundColor() { + @computed public static get userBackgroundColor() { return StrCast(Doc.UserDoc().userBackgroundColor); } @@ -150,35 +149,35 @@ export class SettingsManager extends React.Component<{}> { val: scheme, }))} dropdownType={DropdownType.SELECT} - color={this.userColor} + color={SettingsManager.userColor} fillWidth /> {userTheme === ColorScheme.Custom && ( } - selectedColor={this.userColor} + selectedColor={SettingsManager.userColor} setSelectedColor={this.switchUserColor} setFinalColor={this.switchUserColor} /> } - selectedColor={this.userBackgroundColor} + selectedColor={SettingsManager.userBackgroundColor} setSelectedColor={this.switchUserBackgroundColor} setFinalColor={this.switchUserBackgroundColor} /> } - selectedColor={this.userVariantColor} + selectedColor={SettingsManager.userVariantColor} setSelectedColor={this.switchUserVariantColor} setFinalColor={this.switchUserVariantColor} /> @@ -198,7 +197,7 @@ export class SettingsManager extends React.Component<{}> { onClick={e => (Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'author_date')} toggleStatus={Doc.UserDoc().layout_showTitle !== undefined} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> { onClick={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} toggleStatus={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} - toggleStatus={FontIconBox.GetShowLabels()} + onClick={e => Doc.SetShowIconLabels(!Doc.GetShowIconLabels())} + toggleStatus={Doc.GetShowIconLabels()} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> FontIconBox.SetRecognizeGestures(!FontIconBox.GetRecognizeGestures())} - toggleStatus={FontIconBox.GetRecognizeGestures()} + onClick={e => Doc.SetRecognizeGestures(!Doc.GetRecognizeGestures())} + toggleStatus={Doc.GetRecognizeGestures()} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> { onClick={e => (Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)} toggleStatus={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> { onClick={e => (Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)} toggleStatus={BoolCast(Doc.UserDoc().openInkInLightbox)} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} /> { onClick={e => (Doc.UserDoc().showLinkLines = !Doc.UserDoc().showLinkLines)} toggleStatus={BoolCast(Doc.UserDoc().showLinkLines)} size={Size.XSMALL} - color={this.userColor} + color={SettingsManager.userColor} />
); @@ -284,7 +283,7 @@ export class SettingsManager extends React.Component<{}> {
{/* */} - {}} /> + {}} /> { return { @@ -301,7 +300,7 @@ export class SettingsManager extends React.Component<{}> { setSelectedVal={val => { this.changeFontFamily(val as string); }} - color={this.userColor} + color={SettingsManager.userColor} fillWidth /> @@ -329,12 +328,12 @@ export class SettingsManager extends React.Component<{}> { @computed get passwordContent() { return (
- this.changeVal(val as string, 'curr')} fillWidth password /> - this.changeVal(val as string, 'new')} fillWidth password /> - this.changeVal(val as string, 'conf')} fillWidth password /> + this.changeVal(val as string, 'curr')} fillWidth password /> + this.changeVal(val as string, 'new')} fillWidth password /> + this.changeVal(val as string, 'conf')} fillWidth password /> {!this.passwordResultText ? null :
{this.passwordResultText}
} -
); } @@ -394,10 +393,10 @@ export class SettingsManager extends React.Component<{}> { dropdownType={DropdownType.SELECT} type={Type.TERT} placement="bottom-start" - color={this.userColor} + color={SettingsManager.userColor} fillWidth /> - +
Freeform Navigation @@ -422,15 +421,21 @@ export class SettingsManager extends React.Component<{}> { dropdownType={DropdownType.SELECT} type={Type.TERT} placement="bottom-start" - color={this.userColor} + color={SettingsManager.userColor} />
Permissions
-
@@ -449,7 +454,7 @@ export class SettingsManager extends React.Component<{}> { ]; return (
-
+
{tabs.map(tab => { const isActive = this.activeTab === tab.title; @@ -457,8 +462,8 @@ export class SettingsManager extends React.Component<{}> {
(this.activeTab = tab.title))}> @@ -469,19 +474,19 @@ export class SettingsManager extends React.Component<{}> {
-
{DashVersion}
-
+
{DashVersion}
+
{Doc.CurrentUserEmail}
-
-
-
+
{tabs.map(tab => (
{tab.ele} diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 81ddeb9e3..9a9097bf7 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -18,10 +18,10 @@ import { DictationOverlay } from '../views/DictationOverlay'; import { MainViewModal } from '../views/MainViewModal'; import { DocumentView } from '../views/nodes/DocumentView'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; -import { SearchBox } from '../views/search/SearchBox'; import { DocumentManager } from './DocumentManager'; import { GroupManager, UserOptions } from './GroupManager'; import { GroupMemberView } from './GroupMemberView'; +import { SearchUtil } from './SearchUtil'; import { SelectionManager } from './SelectionManager'; import { SettingsManager } from './SettingsManager'; import './SharingManager.scss'; @@ -446,7 +446,7 @@ export class SharingManager extends React.Component<{}> { if (this.myDocAcls) { const newDocs: Doc[] = []; - SearchBox.foreachRecursiveDoc(docs, (depth, doc) => newDocs.push(doc)); + SearchUtil.foreachRecursiveDoc(docs, (depth, doc) => newDocs.push(doc)); docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin); } @@ -527,10 +527,10 @@ export class SharingManager extends React.Component<{}> { const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-'; return !permissions ? null : ( -
+
{StrCast(group.title)}
  - {group instanceof Doc ? } size={Size.XSMALL} color={SettingsManager.Instance.userColor} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null} + {group instanceof Doc ? } size={Size.XSMALL} color={SettingsManager.userColor} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null}
{admin || this.myDocAcls ? ( diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index c194ede32..a2f9de9ab 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -1,13 +1,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Size, Type, isDark } from 'browndash-components'; +import { Button, IconButton, isDark, Size, Type } from 'browndash-components'; import { action, computed, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { FaBug, FaCamera, FaStamp } from 'react-icons/fa'; +import { FaBug, FaCamera } from 'react-icons/fa'; import { Doc, DocListCast } from '../../../fields/Doc'; import { AclAdmin, DashVersion } from '../../../fields/DocSymbols'; import { StrCast } from '../../../fields/Types'; import { GetEffectiveAcl } from '../../../fields/util'; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { PingManager } from '../../util/PingManager'; import { ReportManager } from '../../util/reportManager/ReportManager'; @@ -15,13 +16,12 @@ import { ServerStats } from '../../util/ServerStats'; import { SettingsManager } from '../../util/SettingsManager'; import { SharingManager } from '../../util/SharingManager'; import { UndoManager } from '../../util/UndoManager'; +import { CollectionDockingView } from '../collections/CollectionDockingView'; import { ContextMenu } from '../ContextMenu'; import { DashboardView } from '../DashboardView'; -import { MainView } from '../MainView'; -import { CollectionDockingView } from '../collections/CollectionDockingView'; import { Colors } from '../global/globalEnums'; +import { DocumentView } from '../nodes/DocumentView'; import './TopBar.scss'; -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; /** * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user @@ -43,7 +43,7 @@ export class TopBar extends React.Component { return StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE); } @computed get backgroundColor() { - return PingManager.Instance.IsBeating ? SettingsManager.Instance.userBackgroundColor : Colors.MEDIUM_GRAY; + return PingManager.Instance.IsBeating ? SettingsManager.userBackgroundColor : Colors.MEDIUM_GRAY; } @observable happyHeart: boolean = PingManager.Instance.IsBeating; @@ -76,9 +76,7 @@ export class TopBar extends React.Component { dash
)} - {Doc.ActiveDashboard && ( -
); } diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx index 02e44a793..524492226 100644 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -6,8 +6,8 @@ import { observer } from 'mobx-react'; import { Doc } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import '../../views/nodes/WebBox.scss'; -import { DocumentDecorations } from '../DocumentDecorations'; import { CollectionFreeFormDocumentViewProps } from '../nodes/CollectionFreeFormDocumentView'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import './DashWebRTCVideo.scss'; import { hangup, initialize, refreshVideos } from './WebCamLogic'; @@ -71,8 +71,8 @@ export class DashWebRTCVideo extends React.Component ); - const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? '-interactive' : ''); + const frozen = !this.props.isSelected() || DocumentView.Interacting; + const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentView.Interacting ? '-interactive' : ''); return ( <> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 4ad38e7fc..f17e10d9e 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -47,7 +47,7 @@ import { FieldId, RefField } from './RefField'; import { RichTextField } from './RichTextField'; import { listSpec } from './Schema'; import { ComputedField, ScriptField } from './ScriptField'; -import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types'; +import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types'; import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField'; import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions } from './util'; import JSZip = require('jszip'); @@ -156,7 +156,26 @@ export function updateCachedAcls(doc: Doc) { @scriptingGlobal @Deserializable('Doc', updateCachedAcls, ['id']) export class Doc extends RefField { - @observable public static CurrentlyLoading: Doc[]; + @observable public static RecordingEvent = 0; + + // this isn't really used at the moment, but is intended to indicate whether ink stroke are passed through a gesture recognizer + static GetRecognizeGestures() { + return BoolCast(Doc.UserDoc()._recognizeGestures); + } + static SetRecognizeGestures(show: boolean) { + Doc.UserDoc()._recognizeGestures = show; + } + + // + // This controls whether fontIconButtons will display labels under their icons or not + // + static GetShowIconLabels() { + return BoolCast(Doc.UserDoc()._showLabel); + } + static SetShowIconLabels(show: boolean) { + Doc.UserDoc()._showLabel = show; + } + @observable public static CurrentlyLoading: Doc[] = []; // this assignment doesn't work. the actual assignment happens in DocumentManager's constructor // removes from currently loading display @action public static removeCurrentlyLoading(doc: Doc) { @@ -169,9 +188,6 @@ export class Doc extends RefField { // adds doc to currently loading display @action public static addCurrentlyLoading(doc: Doc) { - if (!Doc.CurrentlyLoading) { - Doc.CurrentlyLoading = []; - } if (Doc.CurrentlyLoading.indexOf(doc) === -1) { Doc.CurrentlyLoading.push(doc); } diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index 24cd078f2..5ecf25e08 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -2,20 +2,20 @@ import { AssertionError } from 'assert'; import { docs_v1 } from 'googleapis'; import { Fragment, Mark, Node } from 'prosemirror-model'; import { sinkListItem } from 'prosemirror-schema-list'; -import { Utils, DashColor } from '../Utils'; -import { Docs, DocUtils } from '../client/documents/Documents'; -import { schema } from '../client/views/nodes/formattedText/schema_rts'; +import { EditorState, TextSelection, Transaction } from 'prosemirror-state'; +import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils'; import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from '../client/DocServer'; +import { Docs, DocUtils } from '../client/documents/Documents'; import { Networking } from '../client/Network'; import { FormattedTextBox } from '../client/views/nodes/formattedText/FormattedTextBox'; +import { schema } from '../client/views/nodes/formattedText/schema_rts'; +import { DashColor, Utils } from '../Utils'; import { Doc, Opt } from './Doc'; import { Id } from './FieldSymbols'; import { RichTextField } from './RichTextField'; import { Cast, StrCast } from './Types'; import Color = require('color'); -import { EditorState, TextSelection, Transaction } from 'prosemirror-state'; -import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils'; export namespace RichTextUtils { const delimiter = '\n'; -- cgit v1.2.3-70-g09d2 From 3f51ab416090a249aab489b3eb21a456b4d42143 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 21:44:25 -0400 Subject: fixed exceptions when delete entities from a Map when it's in the process of unmounting --- src/client/views/nodes/MapBox/MapBox.tsx | 15 ++++++++++++--- src/client/views/nodes/MapBox/MapPushpinBox.tsx | 1 - 2 files changed, 12 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 6f552953d..f0106dbbb 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -98,10 +98,13 @@ export class MapBox extends ViewBoxAnnotatableComponent this._disposers[key]?.()); } @@ -343,10 +346,14 @@ export class MapBox extends ViewBoxAnnotatableComponent this.pushpinClicked(temp as Doc)); - this._bingMap.current.entities.push(newpin); + if (!this._unmounting) { + this._bingMap.current.entities.push(newpin); + } this.map_docToPinMap.set(temp, newpin); this.selectedPin = undefined; this.bingSearchBarContents = this.rootDoc.map; @@ -488,7 +495,9 @@ export class MapBox extends ViewBoxAnnotatableComponent { - this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc)); + if (!this._unmounting) { + this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc)); + } this.map_docToPinMap.delete(pinDoc); this.selectedPin = undefined; }; diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx index 552bceace..42bada0ef 100644 --- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx +++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx @@ -15,7 +15,6 @@ export class MapPushpinBox extends ViewBoxBaseComponent() { return FieldView.LayoutString(MapPushpinBox, fieldKey); } componentDidMount() { - // if (this.mapBoxView) this.mapBoxView.addPushpin(this.rootDoc); } componentWillUnmount() { -- cgit v1.2.3-70-g09d2 From ae9ec0179e556f44bb9404f319d326321e73cb1f Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 22:15:27 -0400 Subject: fixed coloring of Select box in properties view for filters --- src/client/views/FilterPanel.tsx | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index b61ff35c7..0d4f4df5a 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -15,6 +15,7 @@ import { undoable } from '../util/UndoManager'; import './FilterPanel.scss'; import { FieldView } from './nodes/FieldView'; import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; +import { SettingsManager } from '../util/SettingsManager'; interface filterProps { rootDoc: Doc; @@ -278,7 +279,41 @@ export class FilterPanel extends React.Component {
- ({ + ...baseStyles, + color: SettingsManager.userColor, + background: SettingsManager.userBackgroundColor, + }), + placeholder: (baseStyles, state) => ({ + ...baseStyles, + color: SettingsManager.userColor, + background: SettingsManager.userBackgroundColor, + }), + input: (baseStyles, state) => ({ + ...baseStyles, + color: SettingsManager.userColor, + background: SettingsManager.userBackgroundColor, + }), + option: (baseStyles, state) => ({ + ...baseStyles, + color: SettingsManager.userColor, + background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor, + }), + menuList: (baseStyles, state) => ({ + ...baseStyles, + backgroundColor: SettingsManager.userBackgroundColor, + }), + }} + placeholder="Add a filter..." + options={options} + isMulti={false} + onChange={val => this.facetClick((val as UserOptions).value)} + onKeyDown={e => e.stopPropagation()} + value={null} + closeMenuOnSelect={true} + />
{/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */} {/*
-- cgit v1.2.3-70-g09d2 From f82baffbe4269deb257604a8203e10d727ff0a1b Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 28 Aug 2023 02:55:30 -0400 Subject: trying to fix more colors --- package-lock.json | 154 ++++++++------------- package.json | 2 +- src/Utils.ts | 4 +- src/client/views/MainView.scss | 4 +- .../views/PropertiesDocBacklinksSelector.tsx | 3 +- src/client/views/PropertiesView.tsx | 30 +++- src/client/views/UndoStack.tsx | 12 +- .../collectionLinear/CollectionLinearView.scss | 2 +- .../collectionLinear/CollectionLinearView.tsx | 6 +- src/client/views/linking/LinkMenu.scss | 1 - src/client/views/linking/LinkMenu.tsx | 3 +- src/client/views/linking/LinkMenuGroup.tsx | 4 +- src/client/views/linking/LinkMenuItem.scss | 24 +--- src/client/views/linking/LinkMenuItem.tsx | 12 +- .../views/nodes/DataVizBox/components/PieChart.tsx | 16 +-- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 54 +++++--- src/client/views/nodes/LinkDocPreview.scss | 2 - src/client/views/topbar/TopBar.tsx | 3 +- 18 files changed, 156 insertions(+), 180 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 250ac5f8b..0199d3d31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1455,9 +1455,9 @@ } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.11.tgz", - "integrity": "sha512-7X2vGqH2ZKu7Imx0C+o5OysRwtF/wzdCAqmcD1N1v2Ww8CtOSC+p+VoV76skm47DLvBZ8kBFic+egqxM9S/p4g==", + "version": "7.22.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.12.tgz", + "integrity": "sha512-7XXCVqZtyFWqjDsYDY4T45w4mlx1rf7aOgkc/Ww76xkgBiOlmjPkx36PBLHa1k1rwWvVgYMPsbuVnIamx2ZQJw==", "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -6537,9 +6537,9 @@ } }, "browndash-components": { - "version": "0.0.98", - "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.98.tgz", - "integrity": "sha512-g69eW7TJGYo7w3foy68ebZ3A9+2vk2MExIEIV1pH/rWupm6nriZaP64tOOnmOd96oD8Efrb0KT5D3xQeVlfnjw==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.2.tgz", + "integrity": "sha512-5ONrqd6qYYMjWuayhhdwU4tlrtr+7c0n9MAMXg4jccdd9OxnmdlWpjPzOBYl3slE+mSpKcycwKarwYND+eM0NA==", "requires": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", @@ -7094,97 +7094,12 @@ "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" - } - } - } } } }, @@ -8689,7 +8604,7 @@ } }, "string-width-cjs": { - "version": "npm:string-width-cjs@4.2.3", + "version": "npm:string-width@4.2.3", "bundled": true, "requires": { "emoji-regex": "^8.0.0", @@ -8712,7 +8627,7 @@ } }, "strip-ansi-cjs": { - "version": "npm:strip-ansi-cjs@6.0.1", + "version": "npm:strip-ansi@6.0.1", "bundled": true, "requires": { "ansi-regex": "^5.0.1" @@ -8871,7 +8786,7 @@ } }, "wrap-ansi-cjs": { - "version": "npm:wrap-ansi-cjs@7.0.0", + "version": "npm:wrap-ansi@7.0.0", "bundled": true, "requires": { "ansi-styles": "^4.0.0", @@ -10081,14 +9996,14 @@ } }, "caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==" + "version": "1.0.30001524", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", + "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==" }, "electron-to-chromium": { - "version": "1.4.501", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.501.tgz", - "integrity": "sha512-NCF5hZUg73MEP0guvIM+BjPs9W07UeAuc5XCNqRZZTKJxLjE0ZS/Zo5UsV8bbs2y/jeKRPFPzdWdBfOGEZTXKg==" + "version": "1.4.503", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.503.tgz", + "integrity": "sha512-LF2IQit4B0VrUHFeQkWhZm97KuJSGF2WJqq1InpY+ECpFRkXd8yTIaTtJxsO0OKDmiBYwWqcrNaXOurn2T2wiA==" } } }, @@ -10445,6 +10360,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", @@ -11825,6 +11750,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", @@ -11836,6 +11783,7 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { + "d": "^1.0.1", "ext": "^1.1.2" } }, @@ -27648,6 +27596,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/package.json b/package.json index 576c95192..918c0633f 100644 --- a/package.json +++ b/package.json @@ -178,7 +178,7 @@ "body-parser": "^1.19.2", "bootstrap": "^4.6.1", "brotli": "^1.3.3", - "browndash-components": "^0.0.98", + "browndash-components": "^0.1.02", "browser-assert": "^1.2.1", "bson": "^4.6.1", "canvas": "^2.9.3", diff --git a/src/Utils.ts b/src/Utils.ts index 9a94694a2..29824ba94 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -758,8 +758,8 @@ export function DashColor(color: string) { } export function lightOrDark(color: any) { - if (color === 'transparent') return 'gray'; - if (color.startsWith?.('linear')) return 'black'; + if (color === 'transparent') return Colors.DARK_GRAY; + if (color.startsWith?.('linear')) return Colors.BLACK; const nonAlphaColor = color.startsWith('#') ? (color as string).substring(0, 7) : color.startsWith('rgba') ? color.replace(/,.[^,]*\)/, ')').replace('rgba', 'rgb') : color; const col = DashColor(nonAlphaColor).rgb(); const colsum = col.red() + col.green() + col.blue(); diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 8a7f5132b..c880dde9a 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -78,7 +78,7 @@ h1, .mainView-container-Dark { .lm_header .lm_tab { padding: 0px; - opacity: 0.7; + //opacity: 0.7; box-shadow: none; height: 25px; border-bottom: black solid; @@ -194,7 +194,7 @@ 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; diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index da4438481..91a7b3da3 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -5,6 +5,7 @@ import { Cast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; +import { SettingsManager } from '../util/SettingsManager'; import { LinkMenu } from './linking/LinkMenu'; import { OpenWhere, OpenWhereMod } from './nodes/DocumentView'; import './PropertiesDocBacklinksSelector.scss'; @@ -31,7 +32,7 @@ export class PropertiesDocBacklinksSelector extends React.Component +
{this.props.hideTitle ? null :

Contexts:

}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 83bd1b860..5f9439cbc 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -673,7 +673,14 @@ export class PropertiesView extends React.Component { marginLeft: title === '∠:' ? '39px' : '', }}>
{title}
- setter(e.target.value)} onKeyPress={e => e.stopPropagation()} /> + setter(e.target.value)} + onKeyPress={e => e.stopPropagation()} + />
this.upDownButtons('up', key)))}> @@ -930,7 +937,7 @@ export class PropertiesView extends React.Component { regInput = (key: string, value: any, setter: (val: string) => {}) => { return (
- setter(e.target.value)} /> + setter(e.target.value)} />
this.upDownButtons('up', key)))}> @@ -966,6 +973,7 @@ export class PropertiesView extends React.Component { { className="width-range" type="range" defaultValue={this.markScal} + style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }} min={0} max={10} onChange={action(e => (this.markScal = +e.target.value))} @@ -1369,6 +1378,7 @@ export class PropertiesView extends React.Component { get editRelationship() { return ( { return (