aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json262
-rw-r--r--package.json5
-rw-r--r--src/client/util/CalendarManager.scss62
-rw-r--r--src/client/util/CalendarManager.tsx229
-rw-r--r--src/client/views/DocumentButtonBar.tsx21
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBox.scss3
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx851
-rw-r--r--src/client/views/nodes/MapBox/MapboxApiUtility.ts98
9 files changed, 1003 insertions, 530 deletions
diff --git a/package-lock.json b/package-lock.json
index b0748aae6..9dec742fb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3992,6 +3992,234 @@
}
}
},
+ "@mui/x-date-pickers": {
+ "version": "6.18.5",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.18.5.tgz",
+ "integrity": "sha512-3jImYIWP2Xgi608yzm/Sz1v0MTjQQYdZSQOEIi3dWBfSAU9B06KXDpqlXfRSpTV+rtsnfYIIyiWlz6Ltk7sUWw==",
+ "requires": {
+ "@babel/runtime": "^7.23.2",
+ "@mui/base": "^5.0.0-beta.22",
+ "@mui/utils": "^5.14.16",
+ "@types/react-transition-group": "^4.4.8",
+ "clsx": "^2.0.0",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz",
+ "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
+ "requires": {
+ "regenerator-runtime": "^0.14.0"
+ }
+ },
+ "@floating-ui/react-dom": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz",
+ "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==",
+ "requires": {
+ "@floating-ui/dom": "^1.5.1"
+ }
+ },
+ "@mui/base": {
+ "version": "5.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.27.tgz",
+ "integrity": "sha512-duL37qxihT1N0pW/gyXVezP7SttLkF+cLAs/y6g6ubEFmVadjbnZ45SeF12/vAiKzqwf5M0uFH1cczIPXFZygA==",
+ "requires": {
+ "@babel/runtime": "^7.23.5",
+ "@floating-ui/react-dom": "^2.0.4",
+ "@mui/types": "^7.2.11",
+ "@mui/utils": "^5.15.0",
+ "@popperjs/core": "^2.11.8",
+ "clsx": "^2.0.0",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/types": {
+ "version": "7.2.11",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.11.tgz",
+ "integrity": "sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w=="
+ },
+ "@mui/utils": {
+ "version": "5.15.0",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.0.tgz",
+ "integrity": "sha512-XSmTKStpKYamewxyJ256+srwEnsT3/6eNo6G7+WC1tj2Iq9GfUJ/6yUoB7YXjOD2jTZ3XobToZm4pVz1LBt6GA==",
+ "requires": {
+ "@babel/runtime": "^7.23.5",
+ "@types/prop-types": "^15.7.11",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0"
+ }
+ },
+ "@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
+ },
+ "@types/prop-types": {
+ "version": "15.7.11",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
+ "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
+ },
+ "@types/react-transition-group": {
+ "version": "4.4.10",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
+ "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
+ },
+ "react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+ },
+ "regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ }
+ }
+ },
+ "@mui/x-date-pickers-pro": {
+ "version": "6.18.5",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers-pro/-/x-date-pickers-pro-6.18.5.tgz",
+ "integrity": "sha512-f1JKEpi0cVmsOTab3rnx1W4aEPuKnF9IU3Ib7an75rW321QdwFYTm9+V4ZwWnN0jwDq8+13jjhdDEzfqcX76RA==",
+ "requires": {
+ "@babel/runtime": "^7.23.2",
+ "@mui/base": "^5.0.0-beta.22",
+ "@mui/utils": "^5.14.16",
+ "@mui/x-date-pickers": "6.18.5",
+ "@mui/x-license-pro": "6.10.2",
+ "clsx": "^2.0.0",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz",
+ "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
+ "requires": {
+ "regenerator-runtime": "^0.14.0"
+ }
+ },
+ "@floating-ui/react-dom": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz",
+ "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==",
+ "requires": {
+ "@floating-ui/dom": "^1.5.1"
+ }
+ },
+ "@mui/base": {
+ "version": "5.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.27.tgz",
+ "integrity": "sha512-duL37qxihT1N0pW/gyXVezP7SttLkF+cLAs/y6g6ubEFmVadjbnZ45SeF12/vAiKzqwf5M0uFH1cczIPXFZygA==",
+ "requires": {
+ "@babel/runtime": "^7.23.5",
+ "@floating-ui/react-dom": "^2.0.4",
+ "@mui/types": "^7.2.11",
+ "@mui/utils": "^5.15.0",
+ "@popperjs/core": "^2.11.8",
+ "clsx": "^2.0.0",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/types": {
+ "version": "7.2.11",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.11.tgz",
+ "integrity": "sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w=="
+ },
+ "@mui/utils": {
+ "version": "5.15.0",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.0.tgz",
+ "integrity": "sha512-XSmTKStpKYamewxyJ256+srwEnsT3/6eNo6G7+WC1tj2Iq9GfUJ/6yUoB7YXjOD2jTZ3XobToZm4pVz1LBt6GA==",
+ "requires": {
+ "@babel/runtime": "^7.23.5",
+ "@types/prop-types": "^15.7.11",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0"
+ }
+ },
+ "@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
+ },
+ "@types/prop-types": {
+ "version": "15.7.11",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
+ "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
+ },
+ "clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
+ },
+ "react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+ },
+ "regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ }
+ }
+ },
+ "@mui/x-license-pro": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/@mui/x-license-pro/-/x-license-pro-6.10.2.tgz",
+ "integrity": "sha512-Baw3shilU+eHgU+QYKNPFUKvfS5rSyNJ98pQx02E0gKA22hWp/XAt88K1qUfUMPlkPpvg/uci6gviQSSLZkuKw==",
+ "requires": {
+ "@babel/runtime": "^7.22.6",
+ "@mui/utils": "^5.13.7"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz",
+ "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
+ "requires": {
+ "regenerator-runtime": "^0.14.0"
+ }
+ },
+ "@mui/utils": {
+ "version": "5.15.0",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.0.tgz",
+ "integrity": "sha512-XSmTKStpKYamewxyJ256+srwEnsT3/6eNo6G7+WC1tj2Iq9GfUJ/6yUoB7YXjOD2jTZ3XobToZm4pVz1LBt6GA==",
+ "requires": {
+ "@babel/runtime": "^7.23.5",
+ "@types/prop-types": "^15.7.11",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0"
+ }
+ },
+ "@types/prop-types": {
+ "version": "15.7.11",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
+ "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
+ },
+ "react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+ },
+ "regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ }
+ }
+ },
"@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents.3",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
@@ -7695,6 +7923,16 @@
"@types/reactcss": "*"
}
},
+ "@types/react-date-range": {
+ "version": "1.4.9",
+ "resolved": "https://registry.npmjs.org/@types/react-date-range/-/react-date-range-1.4.9.tgz",
+ "integrity": "sha512-5oVEDW0ElYmY1+YVSzdMUR8stxSI5QrRJCgCFUvuEAV5197t412vimD9aVTW6g4JTaxCnMmB1BdEOT/odpaBxQ==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*",
+ "date-fns": "^2.16.1"
+ }
+ },
"@types/react-datepicker": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-3.1.8.tgz",
@@ -12838,6 +13076,11 @@
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
"integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA=="
},
+ "dayjs": {
+ "version": "1.11.10",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+ "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+ },
"de-indent": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
@@ -27697,6 +27940,17 @@
}
}
},
+ "react-date-range": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/react-date-range/-/react-date-range-1.4.0.tgz",
+ "integrity": "sha512-+9t0HyClbCqw1IhYbpWecjsiaftCeRN5cdhsi9v06YdimwyMR2yYHWcgVn3URwtN/txhqKpEZB6UX1fHpvK76w==",
+ "requires": {
+ "classnames": "^2.2.6",
+ "prop-types": "^15.7.2",
+ "react-list": "^0.8.13",
+ "shallow-equal": "^1.2.1"
+ }
+ },
"react-datepicker": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.8.0.tgz",
@@ -27879,6 +28133,14 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
+ "react-list": {
+ "version": "0.8.17",
+ "resolved": "https://registry.npmjs.org/react-list/-/react-list-0.8.17.tgz",
+ "integrity": "sha512-pgmzGi0G5uGrdHzMhgO7KR1wx5ZXVvI3SsJUmkblSAKtewIhMwbQiMuQiTE83ozo04BQJbe0r3WIWzSO0dR1xg==",
+ "requires": {
+ "prop-types": "15"
+ }
+ },
"react-loader-spinner": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-5.3.4.tgz",
diff --git a/package.json b/package.json
index 5d53a6c4e..58a25a68c 100644
--- a/package.json
+++ b/package.json
@@ -72,6 +72,7 @@
"@types/react": "^18.0.15",
"@types/react-autosuggest": "^9.3.14",
"@types/react-color": "^2.17.6",
+ "@types/react-date-range": "^1.4.9",
"@types/react-datepicker": "^3.1.8",
"@types/react-dom": "^18.0.6",
"@types/react-grid-layout": "^1.3.2",
@@ -148,6 +149,8 @@
"@material-ui/core": "^4.12.3",
"@mui/icons-material": "^5.11.16",
"@mui/material": "^5.13.1",
+ "@mui/x-date-pickers": "^6.18.5",
+ "@mui/x-date-pickers-pro": "^6.18.5",
"@octokit/core": "^4.0.4",
"@react-google-maps/api": "^2.7.0",
"@react-three/fiber": "^6.2.3",
@@ -201,6 +204,7 @@
"csv-parser": "^3.0.0",
"csv-stringify": "^6.3.0",
"d3": "^7.8.5",
+ "dayjs": "^1.11.10",
"debounce": "^2.0.0",
"depcheck": "^0.9.2",
"equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
@@ -296,6 +300,7 @@
"react-chartjs-2": "^4.3.0",
"react-color": "^2.19.3",
"react-compound-slider": "^2.5.0",
+ "react-date-range": "^1.4.0",
"react-datepicker": "^3.8.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
diff --git a/src/client/util/CalendarManager.scss b/src/client/util/CalendarManager.scss
new file mode 100644
index 000000000..60610f298
--- /dev/null
+++ b/src/client/util/CalendarManager.scss
@@ -0,0 +1,62 @@
+.calendar-interface{
+ width: 600px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+ gap: 7px;
+ padding: 10px;
+
+ .selected-doc-title{
+ font-size: 1.4em;
+ }
+
+ .creation-type-container{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ align-self: center;
+ gap: 12px;
+
+ .calendar-creation{
+ cursor: pointer;
+ }
+
+ .calendar-creation-selected{
+ border-bottom: 2px solid white;
+ }
+ }
+
+ .choose-calendar-container{
+ margin-top: 10px;
+ align-self: center;
+ width: 60%;
+
+ .MuiFilledInput-input{
+ padding: 10px;
+ }
+ }
+
+ .date-range-picker-container{
+ margin-top: 5px;
+ align-self:center;
+
+ .react-date-range{
+
+ }
+ }
+
+ .create-button-container{
+
+ margin-top: 5px;
+ align-self: center;
+
+ .button-content{
+ font-size: 1.2em;
+ padding: 10px;
+ border-radius: 5px;
+ background-color: #EFF2F7;
+ }
+
+ }
+}
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx
new file mode 100644
index 000000000..39ba41652
--- /dev/null
+++ b/src/client/util/CalendarManager.tsx
@@ -0,0 +1,229 @@
+import * as React from 'react';
+import './CalendarManager.scss';
+import { observer } from "mobx-react";
+import { action, computed, observable, runInAction } from 'mobx';
+import { Doc } from '../../fields/Doc';
+import { DocumentView } from '../views/nodes/DocumentView';
+import { DictationOverlay } from '../views/DictationOverlay';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import { MainViewModal } from '../views/MainViewModal';
+import { TextField } from '@material-ui/core';
+import Select from 'react-select';
+import { SettingsManager } from './SettingsManager';
+import { StrCast } from '../../fields/Types';
+import { SelectionManager } from './SelectionManager';
+import { DocumentManager } from './DocumentManager';
+import { DocData } from '../../fields/DocSymbols';
+import { DateRange, Range, RangeKeyDict } from 'react-date-range';
+import { Button } from 'browndash-components';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { IconLookup, faPlus } from '@fortawesome/free-solid-svg-icons';
+import 'react-date-range/dist/styles.css';
+import 'react-date-range/dist/theme/default.css';
+
+type CreationType = 'new-calendar' | 'existing-calendar' | 'manage-calendars';
+
+@observer
+export class CalendarManager extends React.Component<{}> {
+ public static Instance: CalendarManager;
+ @observable private isOpen = false;
+ @observable private targetDoc: Doc | undefined; // the target document
+ @observable private targetDocView: DocumentView | undefined; // the DocumentView of the target doc
+ @observable private dialogueBoxOpacity = 1; // for the modal
+ @observable private overlayOpacity = 0.4; // for the modal
+
+ @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
+
+ @observable private creationType: CreationType = 'new-calendar';
+
+ @observable
+ calendarName: string = "";
+
+ @action
+ setInterationType = (type: CreationType) => {
+ this.creationType = type;
+ }
+
+
+ public open = (target?: DocumentView, target_doc?: Doc) => {
+ console.log('hi');
+ runInAction(() => {
+ this.targetDoc = target_doc || target?.props.Document;
+ this.targetDocView = target;
+ DictationOverlay.Instance.hasActiveModal = true;
+ this.isOpen = this.targetDoc !== undefined;
+ })
+ };
+
+ public close = action(() => {
+ this.isOpen = false;
+ TaskCompletionBox.taskCompleted = false;
+ setTimeout(
+ action(() => {
+ DictationOverlay.Instance.hasActiveModal = false;
+ this.targetDoc = undefined;
+ }), 500
+ );
+ this.layoutDocAcls = false;
+ });
+
+ constructor(props: {}) {
+ super(props);
+ CalendarManager.Instance = this;
+ }
+
+ componentDidMount(): void {
+
+ }
+
+ private focusOn = (contents: string) => {
+ const title = this.targetDoc ? StrCast(this.targetDoc.title) : '';
+ const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc];
+ return (
+ <span
+ className="focus-span"
+ title={title}
+ onClick={() => {
+ if (this.targetDoc && this.targetDocView && docs.length === 1) {
+ DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true });
+ }
+ }}
+ onPointerEnter={action(() => {
+ if (docs.length) {
+ docs.forEach(doc => doc && Doc.BrushDoc(doc));
+ this.dialogueBoxOpacity = 0.1;
+ this.overlayOpacity = 0.1;
+ }
+ })}
+ onPointerLeave={action(() => {
+ if (docs.length) {
+ docs.forEach(doc => doc && Doc.UnBrushDoc(doc));
+ this.dialogueBoxOpacity = 1;
+ this.overlayOpacity = 0.4;
+ }
+ })}>
+ {contents}
+ </span>
+ );
+ };
+
+ @observable
+ selectedDateRange: Range[] = [{
+ startDate: new Date(),
+ endDate: undefined,
+ key: 'selection'
+ }]
+
+ @action
+ setSelectedDateRange = (range: Range[]) => {
+ this.selectedDateRange = range;
+ }
+
+ @computed
+ get createButtonActive() {
+ if (this.calendarName.length === 0) return false // disabled if no calendar name
+ let startDate: Date | undefined;
+ let endDate: Date | undefined;
+ try {
+ startDate = this.selectedDateRange[0].startDate;
+ endDate = this.selectedDateRange[0].endDate;
+ } catch (e: any){
+ return false; // disabled
+ }
+ if (!startDate || !endDate) return false; // disabled if any is undefined
+ return true;
+ }
+
+ @computed
+ get calendarInterface(){
+ let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
+ const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
+
+ const currentDate = new Date();
+
+ return (
+ <div
+ className='calendar-interface'
+ style= {{
+ background: SettingsManager.userBackgroundColor,
+ color: StrCast(Doc.UserDoc().userColor)
+ }}
+ >
+ <p className="selected-doc-title" style={{ color: SettingsManager.userColor }}>
+ <b>{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}</b>
+ </p>
+ <div className='creation-type-container'>
+ <div
+ className={`calendar-creation ${this.creationType === 'new-calendar' ? 'calendar-creation-selected' : ''}`}
+ onClick={(e) => this.setInterationType('new-calendar')}
+ >
+ Add to New Calendar
+ </div>
+ <div
+ className={`calendar-creation ${this.creationType === 'existing-calendar' ? 'calendar-creation-selected' : ''}`}
+ onClick={(e) => this.setInterationType('existing-calendar')}
+ >
+ Add to Existing calendar
+ </div>
+ </div>
+ <div className='choose-calendar-container'>
+ {this.creationType === 'new-calendar' ?
+ <TextField
+ fullWidth
+ placeholder='Enter calendar name...'
+ variant='filled'
+ style={{
+ backgroundColor: 'white',
+ color: 'black',
+ borderRadius: '5px'
+
+ }}
+
+ />
+ :
+ <Select
+ className='existing-calendar-search'
+ placeholder='Search for existing calendar...'
+ isMulti
+ isSearchable
+ >
+
+ </Select>
+ }
+ </div>
+ <div className='date-range-picker-container'>
+ <DateRange
+ className='react-date-range'
+ editableDateInputs={true}
+ // ranges={[selectionRange]}
+ onChange={item => this.setSelectedDateRange([item.selection])}
+ ranges={this.selectedDateRange}
+ // onChange={this.handleSelect}
+ />
+ </div>
+ <div className='create-button-container'>
+ <Button
+ active={this.createButtonActive}
+ text="Add to Calendar"
+ iconPlacement='right'
+ icon={<FontAwesomeIcon icon={faPlus as IconLookup}/>}
+ />
+ </div>
+
+ </div>
+ )
+ }
+
+ render() {
+ return (
+ <MainViewModal
+ contents={this.calendarInterface}
+ isDisplayed={this.isOpen}
+ interactive={true}
+ dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
+ overlayDisplayedOpacity={this.overlayOpacity}
+ closeOnExternalClick={this.close}
+ />
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index bd82f7782..ccd459f0d 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,4 +1,4 @@
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import { action, computed, observable, runInAction } from 'mobx';
@@ -14,6 +14,7 @@ import { DragManager } from '../util/DragManager';
import { IsFollowLinkScript } from '../util/LinkFollower';
import { SelectionManager } from '../util/SelectionManager';
import { SharingManager } from '../util/SharingManager';
+import { CalendarManager } from '../util/CalendarManager';
import { undoBatch, UndoManager } from '../util/UndoManager';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { TabDocView } from './collections/TabDocView';
@@ -28,6 +29,8 @@ import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
import { PinProps } from './nodes/trails';
import { TemplateMenu } from './TemplateMenu';
import React = require('react');
+import { faCalendarDays } from '@fortawesome/free-solid-svg-icons';
+
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -443,6 +446,21 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
);
}
+ @computed
+ get calendarButton(){
+ const targetDoc = this.view0?.props.Document;
+ return !targetDoc ? null : (
+ <Tooltip title={<div className='dash-calendar-button'>Open calendar menu</div>}>
+ <div className="documentButtonBar-icon" style={{ color: 'white' }} onClick={e => {
+ console.log('hi: ', CalendarManager.Instance)
+ CalendarManager.Instance.open(this.view0, targetDoc)}
+ }>
+ <FontAwesomeIcon className="documentdecorations-icon" icon={faCalendarDays as IconLookup}/>
+ </div>
+ </Tooltip>
+ )
+ }
+
@observable _isRecording = false;
_stopFunc: () => void = emptyFunction;
@computed
@@ -606,6 +624,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
{!SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
<div className="documentButtonBar-button">{this.pinButton}</div>
<div className="documentButtonBar-button">{this.recordButton}</div>
+ <div className="documentButtonBar-button">{this.calendarButton}</div>
{!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
{!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : (
<div className="documentButtonBar-button" style={{ display: !considerPush() ? 'none' : '' }}>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 13f7dc896..d45f24930 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -72,6 +72,7 @@ import { PropertiesView } from './PropertiesView';
import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
import { TopBar } from './topbar/TopBar';
import { DirectionsAnchorMenu } from './nodes/MapBox/DirectionsAnchorMenu';
+import { CalendarManager } from '../util/CalendarManager';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
@@ -1038,6 +1039,7 @@ export class MainView extends React.Component {
{this.inkResources}
<DictationOverlay />
<SharingManager />
+ <CalendarManager />
<ServerStats />
<RTFMarkup />
<SettingsManager />
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index e25261729..ba1e99f84 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -88,12 +88,11 @@
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
- position: absolute;
background-color: rgb(187, 187, 187);
padding: 10px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
- width: 100%;
+ position: absolute;
#route-to-animate-title {
font-size: 1.25em;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 348e52d6c..5c4a6203a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -3,7 +3,7 @@ import BingMapsReact from 'bingmaps-react';
// import 'mapbox-gl/dist/mapbox-gl.css';
import { Button, EditableText, IconButton, Size, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, flow, toJS, autorun} from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, flow, toJS, autorun } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
@@ -28,28 +28,27 @@ import { FieldView, FieldViewProps } from '../FieldView';
import { FormattedTextBox } from '../formattedText/FormattedTextBox';
import { PinProps, PresBox } from '../trails';
import { MapAnchorMenu } from './MapAnchorMenu';
-import * as HME from "h264-mp4-encoder";
-import {simd} from 'wasm-feature-detect';
-import {
+import {
Map as MapboxMap,
MapRef,
- Marker,
- ControlPosition,
- FullscreenControl,
- MapProvider,
- MarkerProps,
- NavigationControl,
- ScaleControl,
- ViewState,
+ Marker,
+ ControlPosition,
+ FullscreenControl,
+ MapProvider,
+ MarkerProps,
+ NavigationControl,
+ ScaleControl,
+ ViewState,
ViewStateChangeEvent,
useControl,
GeolocateControl,
Popup,
MapEvent,
Source,
- Layer} from 'react-map-gl';
-import MapboxGeocoder, {GeocoderOptions} from '@mapbox/mapbox-gl-geocoder';
+ Layer,
+} from 'react-map-gl';
+import MapboxGeocoder, { GeocoderOptions } from '@mapbox/mapbox-gl-geocoder';
import debounce from 'debounce';
import './MapBox.scss';
import { NumberLiteralType } from 'typescript';
@@ -57,7 +56,7 @@ import { NumberLiteralType } from 'typescript';
import mapboxgl, { LngLat, LngLatBoundsLike, LngLatLike, MapLayerMouseEvent, MercatorCoordinate } from 'mapbox-gl';
import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, MultiLineString, Position } from 'geojson';
import { MarkerEvent } from 'react-map-gl/dist/esm/types';
-import { MapboxApiUtility, TransportationType} from './MapboxApiUtility';
+import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
@@ -65,7 +64,7 @@ import { IconLookup, faCircleXmark, faFileExport, faGear, faMinus, faPause, faPl
import { MarkerIcons } from './MarkerIcons';
import { SettingsManager } from '../../../util/SettingsManager';
import * as turf from '@turf/turf';
-import * as d3 from "d3";
+import * as d3 from 'd3';
import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility';
import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons';
import { CirclePicker, ColorState } from 'react-color';
@@ -91,24 +90,24 @@ const MAPBOX_FORWARD_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/map
const MAPBOX_REVERSE_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/mapbox.places/';
type PopupInfo = {
- longitude: number,
- latitude: number,
- title: string,
- description: string
-}
+ longitude: number;
+ latitude: number;
+ title: string;
+ description: string;
+};
export type GeocoderControlProps = Omit<GeocoderOptions, 'accessToken' | 'mapboxgl' | 'marker'> & {
- mapboxAccessToken: string,
+ mapboxAccessToken: string;
marker?: Omit<MarkerProps, 'longitude' | 'latitude'>;
position: ControlPosition;
onResult: (...args: any[]) => void;
-}
+};
type MapMarker = {
- longitude: number,
- latitude: number
-}
+ longitude: number;
+ latitude: number;
+};
/**
* Consider integrating later: allows for drawing, circling, making shapes on map
@@ -157,7 +156,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const originalCoordinates: Position[] = JSON.parse(StrCast(this.routeToAnimate.routeCoordinates));
// const index = Math.floor(this.animationPhase * originalCoordinates.length);
const index = this.animationPhase * (originalCoordinates.length - 1); // Calculate the fractional index
- console.log("Animation phase", this.animationPhase);
+ console.log('Animation phase', this.animationPhase);
const startIndex = Math.floor(index);
const endIndex = Math.ceil(index);
let feature: Feature<Geometry, GeoJsonProperties>;
@@ -173,7 +172,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
feature = {
type: 'Feature',
properties: {
- 'routeTitle': StrCast(this.routeToAnimate.title)
+ routeTitle: StrCast(this.routeToAnimate.title),
},
geometry: geometry,
};
@@ -186,7 +185,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const interpolator = d3.interpolateArray(startCoord, endCoord);
const interpolatedCoord = interpolator(fraction);
-
+
const coordinates = originalCoordinates.slice(0, startIndex + 1).concat([interpolatedCoord]);
geometry = {
@@ -196,12 +195,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
feature = {
type: 'Feature',
properties: {
- 'routeTitle': StrCast(this.routeToAnimate.title)
+ routeTitle: StrCast(this.routeToAnimate.title),
},
geometry: geometry,
};
}
-
+
autorun(() => {
const animationUtil = this.animationUtility;
const concattedCoordinates = geometry.coordinates.concat(originalCoordinates.slice(endIndex));
@@ -210,28 +209,28 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
properties: {},
geometry: {
type: 'LineString',
- coordinates: concattedCoordinates
- }
- }
- if (animationUtil){
- animationUtil.setPath(newFeature)
+ coordinates: concattedCoordinates,
+ },
+ };
+ if (animationUtil) {
+ animationUtil.setPath(newFeature);
}
- })
+ });
return feature;
}
- console.log("ERROR");
+ console.log('ERROR');
return {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
- coordinates: []
+ coordinates: [],
},
};
}
@computed get selectedRouteCoordinates(): Position[] {
let coordinates: Position[] = [];
- if (this.routeToAnimate?.routeCoordinates){
+ if (this.routeToAnimate?.routeCoordinates) {
coordinates = JSON.parse(StrCast(this.routeToAnimate.routeCoordinates));
}
return coordinates;
@@ -247,7 +246,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return {
type: 'Feature',
properties: {
- 'routeTitle': routeDoc.title},
+ routeTitle: routeDoc.title,
+ },
geometry: geometry,
};
});
@@ -274,10 +274,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
componentDidMount() {
this._unmounting = false;
this.props.setContentView?.(this);
-
}
-
_unmounting = false;
componentWillUnmount(): void {
this._unmounting = true;
@@ -509,7 +507,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// The pin that is selected
@observable selectedPinOrRoute: Doc | undefined;
-
@action
deselectPinOrRoute = () => {
if (this.selectedPinOrRoute) {
@@ -517,7 +514,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
// Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
// Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
-
// const temp = this.selectedPin;
// if (!this._unmounting) {
// this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
@@ -533,7 +529,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
};
-
getView = async (doc: Doc) => {
if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar();
return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
@@ -692,8 +687,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
}, 'delete pin');
-
-
tryHideMapAnchorMenu = (e: PointerEvent) => {
let target = document.elementFromPoint(e.x, e.y);
while (target) {
@@ -711,8 +704,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
centerOnSelectedPin = () => {
if (this.selectedPinOrRoute) {
this._mapRef.current?.flyTo({
- center: [NumCast(this.selectedPinOrRoute.longitude), NumCast(this.selectedPinOrRoute.latitude)]
- })
+ center: [NumCast(this.selectedPinOrRoute.longitude), NumCast(this.selectedPinOrRoute.latitude)],
+ });
}
// if (this.selectedPin) {
// this.dataDoc.latitude = this.selectedPin.latitude;
@@ -733,7 +726,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
mapTypeId: 'grayscale',
};
-
/**
* Map options
*/
@@ -752,8 +744,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
},
};
-
-
recolorPin = (pin: Doc, color?: string) => {
// this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin));
// this.map_docToPinMap.delete(pin);
@@ -829,22 +819,24 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setupMoveUpEvents(
e,
e,
- e => { // move event
+ e => {
+ // move event
if (!dragClone) {
- dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement; // copy draggable pin
+ dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement; // copy draggable pin
dragClone.style.position = 'absolute';
dragClone.style.zIndex = '10000';
- DragManager.Root().appendChild(dragClone); // add clone to root
+ DragManager.Root().appendChild(dragClone); // add clone to root
}
dragClone.style.transform = `translate(${e.clientX - 15}px, ${e.clientY - 15}px)`;
return false;
},
- e => { // up event
+ e => {
+ // up event
if (!dragClone) return;
DragManager.Root().removeChild(dragClone);
let target = document.elementFromPoint(e.x, e.y); // element for specified x and y coordinates
while (target) {
- if (target === this._ref.current) {
+ 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] - 20 /* height of search bar */ - this.props.PanelHeight() / 2;
@@ -864,7 +856,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
};
- // incrementer: number = 0;
+ // incrementer: number = 0;
/*
* Creates Pushpin doc and adds it to the list of annotations
*/
@@ -877,13 +869,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
false,
[],
{
- title: location ?? `lat=${NumCast(latitude)},lng=${NumCast(longitude)}`,
- map: location,
- description: "",
+ title: location ?? `lat=${NumCast(latitude)},lng=${NumCast(longitude)}`,
+ map: location,
+ description: '',
wikiData: wikiData,
markerType: 'MAP_PIN',
- markerColor: '#ff5722'
- },
+ markerColor: '#ff5722',
+ }
// { title: map ?? `lat=${latitude},lng=${longitude}`, map: map },
// ,'pushpinIDamongus'+ this.incrementer++
);
@@ -895,60 +887,43 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}, 'createpin');
@action
- createMapRoute = undoable((coordinates: Position[], origin: string, destination: any, createPinForDestination: boolean) => {
- const mapRoute = Docs.Create.MapRouteDocument(
- false,
- [],
- {title: `${origin} --> ${destination.place_name}`, routeCoordinates: JSON.stringify(coordinates)},
- );
- this.addDocument(mapRoute, this.annotationKey);
- if (createPinForDestination) {
- this.createPushpin(destination.center[1], destination.center[0], destination.place_name);
+ createMapRoute = undoable((coordinates: Position[], originName: string, destination: any, createPinForDestination: boolean) => {
+ if (originName !== destination.place_name) {
+ const mapRoute = Docs.Create.MapRouteDocument(false, [], { title: `${originName} --> ${destination.place_name}`, routeCoordinates: JSON.stringify(coordinates) });
+ this.addDocument(mapRoute, this.annotationKey);
+ if (createPinForDestination) {
+ this.createPushpin(destination.center[1], destination.center[0], destination.place_name);
+ }
+ return mapRoute;
}
- return mapRoute;
-
- // mapMarker.infoWindowOpen = true;
+ // TODO: Display error that can't create route to same location
}, 'createmaproute');
searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch();
-
-
-
-
-
@observable
featuresFromGeocodeResults: any[] = [];
-
@action
addMarkerForFeature = (feature: any) => {
const location = feature.place_name;
- if (feature.center){
+ if (feature.center) {
const longitude = feature.center[0];
const latitude = feature.center[1];
const wikiData = feature.properties?.wikiData;
-
- this.createPushpin(
- latitude,
- longitude,
- location,
- wikiData
- )
-
- if (this._mapRef.current){
+
+ this.createPushpin(latitude, longitude, location, wikiData);
+
+ if (this._mapRef.current) {
this._mapRef.current.flyTo({
- center: feature.center
+ center: feature.center,
});
}
this.featuresFromGeocodeResults = [];
-
} else {
// TODO: handle error
}
- }
-
-
+ };
/**
* Makes a forward geocoding API call to Mapbox to retrieve locations based on the search input
@@ -956,12 +931,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
*/
handleSearchChange = async (searchText: string) => {
const features = await MapboxApiUtility.forwardGeocodeForFeatures(searchText);
- if (features && !this.isAnimating){
+ if (features && !this.isAnimating) {
runInAction(() => {
- this.settingsOpen= false;
+ this.settingsOpen = false;
this.featuresFromGeocodeResults = features;
this.routeToAnimate = undefined;
- })
+ });
}
// try {
// const url = MAPBOX_FORWARD_GEOCODE_BASE_URL + encodeURI(searchText) +'.json' +`?access_token=${MAPBOX_ACCESS_TOKEN}`;
@@ -971,34 +946,32 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// this.featuresFromGeocodeResults = data.features;
// })
// } catch (error: any){
- // // TODO: handle error in better way
+ // // TODO: handle error in better way
// console.log(error);
// }
- }
+ };
// @action
// debouncedCall = React.useCallback(debounce(this.debouncedOnSearchBarChange, 300), []);
-
@action
handleMapClick = (e: MapLayerMouseEvent) => {
- if (this._mapRef.current){
- const features = this._mapRef.current.queryRenderedFeatures(
- e.point, {
- layers: ['map-routes-layer']
- }
- );
+ this.featuresFromGeocodeResults = [];
+ if (this._mapRef.current) {
+ const features = this._mapRef.current.queryRenderedFeatures(e.point, {
+ layers: ['map-routes-layer'],
+ });
console.error(features);
if (features && features.length > 0 && features[0].properties && features[0].geometry) {
const geometry = features[0].geometry as LineString;
const routeTitle: string = features[0].properties['routeTitle'];
- const routeDoc: Doc | undefined = this.allRoutes.find((routeDoc) => routeDoc.title === routeTitle);
+ const routeDoc: Doc | undefined = this.allRoutes.find(routeDoc => routeDoc.title === routeTitle);
this.deselectPinOrRoute(); // TODO: Also deselect route if selected
- if (routeDoc){
+ if (routeDoc) {
this.selectedPinOrRoute = routeDoc;
Doc.setDocFilter(this.rootDoc, LinkedTo, `mapRoute=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check');
- // TODO: Recolor route
+ // TODO: Recolor route
MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute;
MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
@@ -1008,9 +981,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
MapAnchorMenu.Instance.setRouteDoc(routeDoc);
// TODO: Subject to change
- MapAnchorMenu.Instance.setAllMapboxPins(
- this.allAnnotations.filter(anno => !anno.layout_unrendered)
- )
+ MapAnchorMenu.Instance.setAllMapboxPins(this.allAnnotations.filter(anno => !anno.layout_unrendered));
MapAnchorMenu.Instance.DisplayRoute = this.displayRoute;
MapAnchorMenu.Instance.HideRoute = this.hideRoute;
@@ -1025,16 +996,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
MapAnchorMenu.Instance.jumpTo(e.originalEvent.clientX, e.originalEvent.clientY, true);
document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
- }
+ }
}
}
- }
-
+ };
/**
* Makes a reverse geocoding API call to retrieve features corresponding to a map click (based on longitude
- * and latitude). Sets the search results accordingly.
- * @param e
+ * and latitude). Sets the search results accordingly.
+ * @param e
*/
handleMapDblClick = async (e: MapLayerMouseEvent) => {
e.preventDefault();
@@ -1043,13 +1013,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const latitude: number = lngLat.lat;
const features = await MapboxApiUtility.reverseGeocodeForFeatures(longitude, latitude);
- if (features){
+ if (features) {
runInAction(() => {
this.featuresFromGeocodeResults = features;
- })
+ });
}
- // // REVERSE GEOCODE TO GET LOCATION DETAILS
+ // // REVERSE GEOCODE TO GET LOCATION DETAILS
// try {
// const url = MAPBOX_REVERSE_GEOCODE_BASE_URL + encodeURI(longitude.toString() + "," + latitude.toString()) + '.json' +
// `?access_token=${MAPBOX_ACCESS_TOKEN}`;
@@ -1063,9 +1033,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// // TODO: handle error in better way
// console.log(error);
// }
- }
+ };
- @observable
+ @observable
currentPopup: PopupInfo | undefined = undefined;
@action
@@ -1080,18 +1050,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check');
this.recolorPin(this.selectedPinOrRoute, 'green'); // TODO: check this method
-
-
+
MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute;
MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
- // pass in the pinDoc
+ // pass in the pinDoc
MapAnchorMenu.Instance.setPinDoc(pinDoc);
- MapAnchorMenu.Instance.setAllMapboxPins(
- this.allAnnotations.filter(anno => !anno.layout_unrendered)
- )
+ MapAnchorMenu.Instance.setAllMapboxPins(this.allAnnotations.filter(anno => !anno.layout_unrendered));
MapAnchorMenu.Instance.DisplayRoute = this.displayRoute;
MapAnchorMenu.Instance.HideRoute = this.hideRoute;
@@ -1106,22 +1073,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
-
- // this._mapRef.current.flyTo({
- // center: [NumCast(pinDoc.longitude), NumCast(pinDoc.latitude)-3]
- // })
-
+ // this._mapRef.current.flyTo({
+ // center: [NumCast(pinDoc.longitude), NumCast(pinDoc.latitude)-3]
+ // })
};
@observable
temporaryRouteSource: FeatureCollection = {
type: 'FeatureCollection',
- features: []
- }
+ features: [],
+ };
@action
displayRoute = (routeInfoMap: Record<TransportationType, any> | undefined, type: TransportationType) => {
- if (routeInfoMap){
+ if (routeInfoMap) {
const newTempRouteSource: FeatureCollection = {
type: 'FeatureCollection',
features: [
@@ -1130,16 +1095,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
properties: {},
geometry: {
type: 'LineString',
- coordinates: routeInfoMap[type].coordinates
- }
- }
- ]
- }
- // TODO: Create pin for destination
- // TODO: Fly to point where full route will be shown
+ coordinates: routeInfoMap[type].coordinates,
+ },
+ },
+ ],
+ };
+ // TODO: Create pin for destination
+ // TODO: Fly to point where full route will be shown
this.temporaryRouteSource = newTempRouteSource;
}
- }
+ };
@observable
isAnimating: boolean = false;
@@ -1147,10 +1112,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable
routeToAnimate: Doc | undefined = undefined;
- @observable
+ @observable
animationPhase: number = 0;
- @observable
+ @observable
finishedFlyTo: boolean = false;
@action
@@ -1158,73 +1123,72 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.animationPhase = newValue;
};
- @observable
+ @observable
frameId: number | null = null;
@action
setFrameId = (frameId: number) => {
this.frameId = frameId;
- }
+ };
- @observable
+ @observable
animationUtility: AnimationUtility | null = null;
@action
setAnimationUtility = (util: AnimationUtility) => {
this.animationUtility = util;
- }
+ };
@action
openAnimationPanel = (routeDoc: Doc | undefined) => {
- if (routeDoc){
+ if (routeDoc) {
MapAnchorMenu.Instance.fadeOut(true);
document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
+ this.featuresFromGeocodeResults = [];
this.routeToAnimate = routeDoc;
}
- }
+ };
@observable
mapboxMapViewState: ViewState = {
zoom: this.dataDoc.map_zoom ? NumCast(this.dataDoc.map_zoom) : 8,
- longitude: this.dataDoc.longitude ? NumCast(this.dataDoc.longitude) : -71.4128,
- latitude: this.dataDoc.latitude ? NumCast(this.dataDoc.latitude) : 41.8240,
+ longitude: this.dataDoc.longitude ? NumCast(this.dataDoc.longitude) : -71.4128,
+ latitude: this.dataDoc.latitude ? NumCast(this.dataDoc.latitude) : 41.824,
pitch: this.dataDoc.map_pitch ? NumCast(this.dataDoc.map_pitch) : 0,
bearing: this.dataDoc.map_bearing ? NumCast(this.dataDoc.map_bearing) : 0,
padding: {
top: 0,
bottom: 0,
left: 0,
- right: 0
+ right: 0,
},
- }
+ };
- @computed
+ @computed
get preAnimationViewState() {
- if (!this.isAnimating){
+ if (!this.isAnimating) {
return this.mapboxMapViewState;
}
}
-
- @observable
+ @observable
isStreetViewAnimation: boolean = false;
- @observable
+ @observable
animationSpeed: AnimationSpeed = AnimationSpeed.MEDIUM;
-
@observable
animationLineColor: string = '#ffff00';
@action
setAnimationLineColor = (color: ColorState) => {
this.animationLineColor = color.hex;
- }
+ };
@action
updateAnimationSpeed = () => {
let newAnimationSpeed: AnimationSpeed;
- switch (this.animationSpeed){
+ switch (this.animationSpeed) {
case AnimationSpeed.SLOW:
newAnimationSpeed = AnimationSpeed.MEDIUM;
break;
@@ -1239,63 +1203,62 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
break;
}
this.animationSpeed = newAnimationSpeed;
- if (this.animationUtility){
+ if (this.animationUtility) {
this.animationUtility.updateAnimationSpeed(newAnimationSpeed);
}
- }
+ };
@computed get animationSpeedTooltipText(): string {
switch (this.animationSpeed) {
- case AnimationSpeed.SLOW:
- return '1x speed';
- case AnimationSpeed.MEDIUM:
- return '2x speed';
- case AnimationSpeed.FAST:
- return '3x speed';
- default:
- return '2x speed';
+ case AnimationSpeed.SLOW:
+ return '1x speed';
+ case AnimationSpeed.MEDIUM:
+ return '2x speed';
+ case AnimationSpeed.FAST:
+ return '3x speed';
+ default:
+ return '2x speed';
}
}
- @computed get animationSpeedIcon(): JSX.Element{
+ @computed get animationSpeedIcon(): JSX.Element {
switch (this.animationSpeed) {
case AnimationSpeed.SLOW:
- return slowSpeedIcon;
+ return slowSpeedIcon;
case AnimationSpeed.MEDIUM:
- return mediumSpeedIcon;
+ return mediumSpeedIcon;
case AnimationSpeed.FAST:
- return fastSpeedIcon;
+ return fastSpeedIcon;
default:
- return mediumSpeedIcon;
- }
+ return mediumSpeedIcon;
+ }
}
@action
toggleIsStreetViewAnimation = () => {
const newVal = !this.isStreetViewAnimation;
this.isStreetViewAnimation = newVal;
- if (this.animationUtility){
- this.animationUtility.updateIsStreetViewAnimation(newVal)
+ if (this.animationUtility) {
+ this.animationUtility.updateIsStreetViewAnimation(newVal);
}
- }
+ };
- @observable
+ @observable
dynamicRouteFeature: Feature<Geometry, GeoJsonProperties> = {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
- coordinates: []
- }
+ coordinates: [],
+ },
};
-
- @observable
+ @observable
path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties> = {
type: 'Feature',
geometry: {
type: 'LineString',
- coordinates: []
+ coordinates: [],
},
- properties: {}
+ properties: {},
};
getFeatureFromRouteDoc = (routeDoc: Doc): Feature<Geometry, GeoJsonProperties> => {
@@ -1306,14 +1269,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return {
type: 'Feature',
properties: {
- 'routeTitle': routeDoc.title},
+ routeTitle: routeDoc.title,
+ },
geometry: geometry,
};
- }
+ };
- @action
+ @action
playAnimation = (status: AnimationStatus) => {
- if (!this._mapRef.current || !this.routeToAnimate){
+ if (!this._mapRef.current || !this.routeToAnimate) {
return;
}
@@ -1322,43 +1286,32 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.finishedFlyTo = AnimationStatus.RESUME ? this.finishedFlyTo : false;
const path = turf.lineString(this.selectedRouteCoordinates);
-
+
this.settingsOpen = false;
this.path = path;
this.isAnimating = true;
runInAction(() => {
- return new Promise<void>(async (resolve) => {
+ return new Promise<void>(async resolve => {
const targetLngLat = {
lng: this.selectedRouteCoordinates[0][0],
lat: this.selectedRouteCoordinates[0][1],
};
- const animationUtil = new AnimationUtility(
- targetLngLat,
- this.selectedRouteCoordinates,
- this.isStreetViewAnimation,
- this.animationSpeed,
- this.showTerrain,
- this._mapRef.current
- );
+ const animationUtil = new AnimationUtility(targetLngLat, this.selectedRouteCoordinates, this.isStreetViewAnimation, this.animationSpeed, this.showTerrain, this._mapRef.current);
runInAction(() => {
this.setAnimationUtility(animationUtil);
- })
-
+ });
const updateFrameId = (newFrameId: number) => {
this.setFrameId(newFrameId);
- }
+ };
- const updateAnimationPhase = (
- newAnimationPhase: number,
- ) => {
+ const updateAnimationPhase = (newAnimationPhase: number) => {
this.setAnimationPhase(newAnimationPhase);
};
-
- if (status !== AnimationStatus.RESUME) {
+ if (status !== AnimationStatus.RESUME) {
const result = await animationUtil.flyInAndRotate({
map: this._mapRef.current!,
// targetLngLat,
@@ -1372,18 +1325,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
updateFrameId,
});
- console.log("Bearing: ", result.bearing);
- console.log("Altitude: ", result.altitude);
-
- }
+ console.log('Bearing: ', result.bearing);
+ console.log('Altitude: ', result.altitude);
+ }
runInAction(() => {
this.finishedFlyTo = true;
- })
+ });
// follow the path while slowly rotating the camera, passing in the camera bearing and altitude from the previous animation
await animationUtil.animatePath({
- map: this._mapRef.current!,
+ map: this._mapRef.current!,
// path: this.path,
// startBearing: -20,
// startAltitude: this.isStreetViewAnimation ? 80 : 12000,
@@ -1392,43 +1344,39 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
updateAnimationPhase,
updateFrameId,
});
-
+
// get the bounds of the linestring, use fitBounds() to animate to a final view
const bbox3d = turf.bbox(this.path);
-
+
const bbox2d: LngLatBoundsLike = [bbox3d[0], bbox3d[1], bbox3d[2], bbox3d[3]];
-
+
this._mapRef.current!.fitBounds(bbox2d, {
duration: 3000,
pitch: 30,
bearing: 0,
padding: 120,
});
-
+
setTimeout(() => {
this.isStreetViewAnimation = false;
resolve();
}, 10000);
});
-
- })
-
-
- }
-
+ });
+ };
- @action
+ @action
pauseAnimation = () => {
- if (this.frameId && this.animationPhase > 0){
+ if (this.frameId && this.animationPhase > 0) {
window.cancelAnimationFrame(this.frameId);
this.frameId = null;
this.isAnimating = false;
}
- }
+ };
@action
stopAnimation = (close: boolean) => {
- if (this.frameId){
+ if (this.frameId) {
window.cancelAnimationFrame(this.frameId);
}
this.animationPhase = 0;
@@ -1441,22 +1389,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.routeToAnimate = undefined;
this.animationUtility = null;
}
- if (this.preAnimationViewState){
+ if (this.preAnimationViewState) {
this.mapboxMapViewState = this.preAnimationViewState;
}
-
-
- }
+ };
@action
- exportAnimationToVideo = () => {
-
- }
+ exportAnimationToVideo = () => {};
getRouteAnimationOptions = (): JSX.Element => {
return (
<>
- <IconButton
+ <IconButton
tooltip={this.isAnimating && this.finishedFlyTo ? 'Pause Animation' : 'Play Animation'}
onPointerDown={() => {
if (this.isAnimating && this.finishedFlyTo) {
@@ -1467,87 +1411,57 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.playAnimation(AnimationStatus.START); // Play from the beginning
}
}}
- icon={this.isAnimating && this.finishedFlyTo ?
- <FontAwesomeIcon icon={faPause as IconLookup}/>
- :
- <FontAwesomeIcon icon={faPlay as IconLookup}/>
- }
- color='black'
- size={Size.MEDIUM}
- />
- {this.isAnimating && this.finishedFlyTo &&
- <IconButton
- tooltip='Restart animation'
- onPointerDown={() => {
- this.stopAnimation(false);
- this.playAnimation(AnimationStatus.START)
- }}
- icon={<FontAwesomeIcon icon={faRotate as IconLookup}/>}
- color='black'
- size={Size.MEDIUM}
- />
-
- }
- <IconButton
- style={{marginRight: '10px'}}
- tooltip='Stop and close animation'
- onPointerDown={() => this.stopAnimation(true)}
- icon={<FontAwesomeIcon icon={faCircleXmark as IconLookup}/>}
- color='black'
+ icon={this.isAnimating && this.finishedFlyTo ? <FontAwesomeIcon icon={faPause as IconLookup} /> : <FontAwesomeIcon icon={faPlay as IconLookup} />}
+ color="black"
size={Size.MEDIUM}
/>
+ {this.isAnimating && this.finishedFlyTo && (
+ <IconButton
+ tooltip="Restart animation"
+ onPointerDown={() => {
+ this.stopAnimation(false);
+ this.playAnimation(AnimationStatus.START);
+ }}
+ icon={<FontAwesomeIcon icon={faRotate as IconLookup} />}
+ color="black"
+ size={Size.MEDIUM}
+ />
+ )}
+ <IconButton style={{ marginRight: '10px' }} tooltip="Stop and close animation" onPointerDown={() => this.stopAnimation(true)} icon={<FontAwesomeIcon icon={faCircleXmark as IconLookup} />} color="black" size={Size.MEDIUM} />
<>
- <div className='animation-suboptions'>
+ <div className="animation-suboptions">
<div>|</div>
<FormControlLabel
- className='first-person-label'
- style={{width: '130px'}}
- label='1st person animation:'
- labelPlacement='start'
- control={
- <Checkbox
- color='success'
- checked={this.isStreetViewAnimation}
- onChange={this.toggleIsStreetViewAnimation}
- />
- }
- />
- <div id='divider'>|</div>
- <IconButton
- tooltip={this.animationSpeedTooltipText}
- onPointerDown={this.updateAnimationSpeed}
- icon={this.animationSpeedIcon}
- size={Size.MEDIUM}
+ className="first-person-label"
+ style={{ width: '130px' }}
+ label="1st person animation:"
+ labelPlacement="start"
+ control={<Checkbox color="success" checked={this.isStreetViewAnimation} onChange={this.toggleIsStreetViewAnimation} />}
/>
- <div id='divider'>|</div>
- <div style={{width: '230px'}}>Select Line Color: </div>
- <CirclePicker
- circleSize={12}
- circleSpacing={5}
- width='100%'
- colors={['#ffff00', '#03a9f4', '#ff0000', '#ff5722', '#000000', '#673ab7']}
- onChange={(color) => this.setAnimationLineColor(color)}
- />
+ <div id="divider">|</div>
+ <IconButton tooltip={this.animationSpeedTooltipText} onPointerDown={this.updateAnimationSpeed} icon={this.animationSpeedIcon} size={Size.MEDIUM} />
+ <div id="divider">|</div>
+ <div style={{ width: '230px' }}>Select Line Color: </div>
+ <CirclePicker circleSize={12} circleSpacing={5} width="100%" colors={['#ffff00', '#03a9f4', '#ff0000', '#ff5722', '#000000', '#673ab7']} onChange={color => this.setAnimationLineColor(color)} />
</div>
- </>
+ </>
</>
- )
- }
+ );
+ };
@action
hideRoute = () => {
this.temporaryRouteSource = {
type: 'FeatureCollection',
- features: []
- }
- }
-
+ features: [],
+ };
+ };
- @observable
+ @observable
settingsOpen: boolean = false;
- @observable
- mapStyle: string = 'mapbox://styles/mapbox/streets-v12'
+ @observable
+ mapStyle: string = 'mapbox://styles/mapbox/streets-v12';
@observable
showTerrain: boolean = true;
@@ -1558,103 +1472,94 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.featuresFromGeocodeResults = [];
this.settingsOpen = !this.settingsOpen;
}
- }
+ };
@action
changeMapStyle = (e: React.ChangeEvent<HTMLSelectElement>) => {
this.dataDoc.map_style = `mapbox://styles/mapbox/${e.target.value}`;
// this.mapStyle = `mapbox://styles/mapbox/${e.target.value}`
- }
+ };
- @action
+ @action
onBearingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const bearing = parseInt(e.target.value);
- if (!isNaN(bearing) && this._mapRef.current){
+ if (!isNaN(bearing) && this._mapRef.current) {
const fixedBearing = Math.max(0, Math.min(360, bearing));
this._mapRef.current.setBearing(fixedBearing);
this.dataDoc.map_bearing = fixedBearing;
this.mapboxMapViewState = {
...this.mapboxMapViewState,
- bearing: fixedBearing
- }
+ bearing: fixedBearing,
+ };
}
- }
+ };
- @action
+ @action
onPitchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const pitch = parseInt(e.target.value);
- if (!isNaN(pitch) && this._mapRef.current){
+ if (!isNaN(pitch) && this._mapRef.current) {
const fixedPitch = Math.max(0, Math.min(85, pitch));
this._mapRef.current.setPitch(fixedPitch);
this.dataDoc.map_pitch = fixedPitch;
this.mapboxMapViewState = {
...this.mapboxMapViewState,
- pitch: fixedPitch
- }
+ pitch: fixedPitch,
+ };
}
- }
+ };
- @action
+ @action
onZoomChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const zoom = parseInt(e.target.value);
- if (!isNaN(zoom) && this._mapRef.current){
+ if (!isNaN(zoom) && this._mapRef.current) {
const fixedZoom = Math.max(0, Math.min(16, zoom));
this._mapRef.current.setZoom(fixedZoom);
this.dataDoc.map_zoom = fixedZoom;
this.mapboxMapViewState = {
...this.mapboxMapViewState,
- zoom: fixedZoom
- }
+ zoom: fixedZoom,
+ };
}
- }
+ };
@action
onStepZoomChange = (increment: boolean) => {
if (this._mapRef.current) {
let newZoom: number;
if (increment) {
- console.log('inc')
+ console.log('inc');
newZoom = this.mapboxMapViewState.zoom + 1;
-
} else {
- console.log('dec')
+ console.log('dec');
newZoom = this.mapboxMapViewState.zoom - 1;
}
this._mapRef.current.setZoom(newZoom);
this.dataDoc.map_zoom = newZoom;
this.mapboxMapViewState = {
...this.mapboxMapViewState,
- zoom: increment ? Math.min(16, newZoom) : Math.max(0, newZoom)
- }
+ zoom: increment ? Math.min(16, newZoom) : Math.max(0, newZoom),
+ };
}
-
- }
-
+ };
@action
onMapMove = (e: ViewStateChangeEvent) => {
this.mapboxMapViewState = e.viewState;
this.dataDoc.longitude = e.viewState.longitude;
this.dataDoc.latitude = e.viewState.latitude;
- }
+ };
@action
toggleShowTerrain = () => {
this.showTerrain = !this.showTerrain;
- }
+ };
getMarkerIcon = (pinDoc: Doc): JSX.Element | null => {
const markerType = StrCast(pinDoc.markerType);
const markerColor = StrCast(pinDoc.markerColor);
return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor) ?? null;
-
- }
-
-
-
-
-
+ };
static _firstRender = true;
static _rerenderDelay = 500;
@@ -1675,7 +1580,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}), MapBox._rerenderDelay);
return null;
}
-
const scale = this.props.NativeDimScaling?.() || 1;
const renderAnnotations = (childFilters?: () => string[]) => null;
@@ -1688,107 +1592,76 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
e.button === 0 && !e.ctrlKey && e.stopPropagation();
}}
style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+ {/* style={{ transformOrigin: "top left", transform: `scale(${scale})`, width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> */}
<div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
{renderAnnotations(this.opaqueFilter)}
{SnappingManager.GetIsDragging() ? null : renderAnnotations()}
- {!this.routeToAnimate &&
+ {!this.routeToAnimate && (
<div className="mapBox-searchbar" style={{ zIndex: 1, position: 'relative', background: 'lightGray' }}>
- <TextField
- fullWidth
- placeholder='Enter a location'
- onChange={(e) => this.handleSearchChange(e.target.value)}
- />
- <IconButton
- icon={<FontAwesomeIcon icon={faGear as IconLookup} size='1x'/>}
- type={Type.TERT}
- onClick={(e) => this.toggleSettings()}
-
- />
- </div>
- }
- {this.settingsOpen && !this.routeToAnimate &&
- <div className='mapbox-settings-panel' style={{right: `${0+ this.sidebarWidth()}px`}}>
- <div className='mapbox-style-select'>
- <div>
- Map Style:
- </div>
+ <TextField fullWidth placeholder="Enter a location" onChange={e => this.handleSearchChange(e.target.value)} />
+ <IconButton icon={<FontAwesomeIcon icon={faGear as IconLookup} size="1x" />} type={Type.TERT} onClick={e => this.toggleSettings()} />
+ </div>
+ )}
+ {this.settingsOpen && !this.routeToAnimate && (
+ <div className="mapbox-settings-panel" style={{ right: `${0 + this.sidebarWidth()}px` }}>
+ <div className="mapbox-style-select">
+ <div>Map Style:</div>
<div>
<select onChange={this.changeMapStyle}>
- <option value='streets-v11'>Streets</option>
- <option value='outdoors-v12'>Outdoors</option>
- <option value='light-v11'>Light</option>
- <option value='dark-v11'>Dark</option>
- <option value='satellite-v9'>Satellite</option>
- <option value='satellite-streets-v12'>Satellite Streets</option>
- <option value='navigation-day-v1'>Navigation Day</option>
- <option value='navigation-night-v1'>Navigation Night</option>
+ <option value="streets-v11">Streets</option>
+ <option value="outdoors-v12">Outdoors</option>
+ <option value="light-v11">Light</option>
+ <option value="dark-v11">Dark</option>
+ <option value="satellite-v9">Satellite</option>
+ <option value="satellite-streets-v12">Satellite Streets</option>
+ <option value="navigation-day-v1">Navigation Day</option>
+ <option value="navigation-night-v1">Navigation Night</option>
</select>
</div>
</div>
- <div className='mapbox-bearing-selection'>
+ <div className="mapbox-bearing-selection">
<div>Bearing: </div>
- <input
- value={NumCast(this.mapboxMapViewState.bearing).toFixed(2)}
- type='number'
- onChange={this.onBearingChange}/>
+ <input value={NumCast(this.mapboxMapViewState.bearing).toFixed(2)} type="number" onChange={this.onBearingChange} />
</div>
- <div className='mapbox-pitch-selection'>
+ <div className="mapbox-pitch-selection">
<div>Pitch: </div>
- <input
- value={NumCast(this.mapboxMapViewState.pitch).toFixed(2)}
- type='number'
- onChange={this.onPitchChange}/>
+ <input value={NumCast(this.mapboxMapViewState.pitch).toFixed(2)} type="number" onChange={this.onPitchChange} />
</div>
- <div className='mapbox-pitch-selection'>
+ <div className="mapbox-pitch-selection">
<div>Zoom: </div>
- <input
- value={NumCast(this.mapboxMapViewState.zoom).toFixed(2)}
- type='number'
- onChange={this.onZoomChange}/>
+ <input value={NumCast(this.mapboxMapViewState.zoom).toFixed(2)} type="number" onChange={this.onZoomChange} />
</div>
- <div className='mapbox-terrain-selection'>
+ <div className="mapbox-terrain-selection">
<div>Show terrain: </div>
- <input
- type='checkbox'
- checked={this.showTerrain}
- onChange={this.toggleShowTerrain}
- />
+ <input type="checkbox" checked={this.showTerrain} onChange={this.toggleShowTerrain} />
</div>
</div>
- }
- {this.routeToAnimate &&
- <div className='animation-panel'>
- <div id='route-to-animate-title'>
- {StrCast(this.routeToAnimate.title)}
- </div>
- <div className='route-animation-options'>
- {this.getRouteAnimationOptions()}
- </div>
+ )}
+ {this.routeToAnimate && (
+ <div className="animation-panel" style={{ width: this.sidebarWidth() === 0 ? '100%' : `calc(100% - ${this.sidebarWidth()}px)` }}>
+ <div id="route-to-animate-title">{StrCast(this.routeToAnimate.title)}</div>
+ <div className="route-animation-options">{this.getRouteAnimationOptions()}</div>
</div>
- }
+ )}
{this.featuresFromGeocodeResults.length > 0 && (
- <div className='mapbox-geocoding-search-results'>
+ <div className="mapbox-geocoding-search-results">
<React.Fragment>
<h4>Choose a location for your pin: </h4>
{this.featuresFromGeocodeResults
.filter(feature => feature.place_name)
.map((feature, idx) => (
- <div
- key={idx}
- className='search-result-container'
- onClick={() => {
- this.handleSearchChange("");
- this.addMarkerForFeature(feature);
- }}
- >
- <div className='search-result-place-name'>
- {feature.place_name}
+ <div
+ key={idx}
+ className="search-result-container"
+ onClick={() => {
+ this.handleSearchChange('');
+ this.addMarkerForFeature(feature);
+ }}>
+ <div className="search-result-place-name">{feature.place_name}</div>
</div>
- </div>
- ))}
+ ))}
</React.Fragment>
-
- </div>
+ </div>
)}
{/* <div className='zoom-box'>
<IconButton // increment
@@ -1812,7 +1685,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
id="mapbox-map"
viewState={{ ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width) / scale, height: NumCast(this.layoutDoc._height) / scale }}
mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'}
- style={{
+ style={{
transformOrigin: 'center',
transform: `scale(${scale < 1 ? 1 : scale})`,
zIndex: '0',
@@ -1823,106 +1696,88 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
onMove={this.onMapMove}
onClick={this.handleMapClick}
onDblClick={this.handleMapDblClick}
- terrain={this.showTerrain ? { source: 'mapbox-dem', exaggeration: 2.0 } : undefined}
-
-
- >
- <Source
- id="mapbox-dem"
- type="raster-dem"
- url="mapbox://mapbox.mapbox-terrain-dem-v1"
- tileSize={512}
- maxzoom={14}
- />
- <Source id='temporary-route' type='geojson' data={this.temporaryRouteSource}/>
- <Source id='map-routes' type='geojson' data={this.allRoutesGeoJson}/>
- <Layer
- id='temporary-route-layer'
- type='line'
- source='temporary-route'
- layout={{"line-join": "round", "line-cap": "round"}}
- paint={{"line-color": "#36454F", "line-width": 4, "line-dasharray": [1,1]}}
- />
- {!this.isAnimating && this.animationPhase == 0 &&
- <Layer
- id='map-routes-layer'
- type='line'
- source='map-routes'
- layout={{"line-join": "round", "line-cap": "round"}}
- paint={{"line-color": "#FF0000", "line-width": 4}}
- />
- }
- {this.routeToAnimate && (this.isAnimating || this.animationPhase > 0) &&
+ terrain={this.showTerrain ? { source: 'mapbox-dem', exaggeration: 2.0 } : undefined}>
+ <Source id="mapbox-dem" type="raster-dem" url="mapbox://mapbox.mapbox-terrain-dem-v1" tileSize={512} maxzoom={14} />
+ <Source id="temporary-route" type="geojson" data={this.temporaryRouteSource} />
+ <Source id="map-routes" type="geojson" data={this.allRoutesGeoJson} />
+ <Layer id="temporary-route-layer" type="line" source="temporary-route" layout={{ 'line-join': 'round', 'line-cap': 'round' }} paint={{ 'line-color': '#36454F', 'line-width': 4, 'line-dasharray': [1, 1] }} />
+ {!this.isAnimating && this.animationPhase == 0 && <Layer id="map-routes-layer" type="line" source="map-routes" layout={{ 'line-join': 'round', 'line-cap': 'round' }} paint={{ 'line-color': '#FF0000', 'line-width': 4 }} />}
+ {this.routeToAnimate && (this.isAnimating || this.animationPhase > 0) && (
<>
- {!this.isStreetViewAnimation &&
+ {!this.isStreetViewAnimation && (
<>
- <Source id='animated-route' type='geojson' data={this.updatedRouteCoordinates}/>
- <Layer
- id='dynamic-animation-line'
- type='line'
- source='animated-route'
- paint={{
- 'line-color': this.animationLineColor,
- 'line-width': 5,
- }}
+ <Source id="animated-route" type="geojson" data={this.updatedRouteCoordinates} />
+ <Layer
+ id="dynamic-animation-line"
+ type="line"
+ source="animated-route"
+ paint={{
+ 'line-color': this.animationLineColor,
+ 'line-width': 5,
+ }}
/>
</>
- }
- <Source id='start-pin-base' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.04)}/>
- <Source id='start-pin-top' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.25)}/>
- <Source id='end-pin-base' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.04)}/>
- <Source id='end-pin-top' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.25)}/>
- <Layer id='start-fill-pin-base' type='fill-extrusion' source='start-pin-base'
+ )}
+ <Source id="start-pin-base" type="geojson" data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.04)} />
+ <Source id="start-pin-top" type="geojson" data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.25)} />
+ <Source id="end-pin-base" type="geojson" data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.04)} />
+ <Source id="end-pin-top" type="geojson" data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.25)} />
+ <Layer
+ id="start-fill-pin-base"
+ type="fill-extrusion"
+ source="start-pin-base"
paint={{
'fill-extrusion-color': '#0bfc03',
- 'fill-extrusion-height': 1000
+ 'fill-extrusion-height': 1000,
}}
/>
- <Layer id='start-fill-pin-top' type='fill-extrusion' source='start-pin-top'
+ <Layer
+ id="start-fill-pin-top"
+ type="fill-extrusion"
+ source="start-pin-top"
paint={{
'fill-extrusion-color': '#0bfc03',
'fill-extrusion-base': 1000,
- 'fill-extrusion-height': 1200
+ 'fill-extrusion-height': 1200,
}}
/>
- <Layer id='end-fill-pin-base' type='fill-extrusion' source='end-pin-base'
+ <Layer
+ id="end-fill-pin-base"
+ type="fill-extrusion"
+ source="end-pin-base"
paint={{
'fill-extrusion-color': '#eb1c1c',
- 'fill-extrusion-height': 1000
+ 'fill-extrusion-height': 1000,
}}
/>
- <Layer id='end-fill-pin-top' type='fill-extrusion' source='end-pin-top'
+ <Layer
+ id="end-fill-pin-top"
+ type="fill-extrusion"
+ source="end-pin-top"
paint={{
'fill-extrusion-color': '#eb1c1c',
'fill-extrusion-base': 1000,
- 'fill-extrusion-height': 1200
+ 'fill-extrusion-height': 1200,
}}
/>
-
</>
- }
-
+ )}
<>
- {!this.isAnimating && this.animationPhase == 0 && this.allPushpins
- // .filter(anno => !anno.layout_unrendered)
- .map((pushpin, idx) => (
- <Marker
- key={idx}
- longitude={NumCast(pushpin.longitude)}
- latitude={NumCast(pushpin.latitude)}
- anchor='bottom'
- onClick={(e: MarkerEvent<mapboxgl.Marker, MouseEvent>) => this.handleMarkerClick(e, pushpin)}
- >
- {this.getMarkerIcon(pushpin)}
- </Marker>
- ))}
+ {!this.isAnimating &&
+ this.animationPhase == 0 &&
+ this.allPushpins
+ // .filter(anno => !anno.layout_unrendered)
+ .map((pushpin, idx) => (
+ <Marker key={idx} longitude={NumCast(pushpin.longitude)} latitude={NumCast(pushpin.latitude)} anchor="bottom" onClick={(e: MarkerEvent<mapboxgl.Marker, MouseEvent>) => this.handleMarkerClick(e, pushpin)}>
+ {this.getMarkerIcon(pushpin)}
+ </Marker>
+ ))}
</>
-
+
{/* {this.mapMarkers.length > 0 && this.mapMarkers.map((marker, idx) => (
<Marker key={idx} longitude={marker.longitude} latitude={marker.latitude}/>
))} */}
-
</MapboxMap>
</MapProvider>
@@ -2002,7 +1857,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
}
-{/* <Autocomplete
+{
+ /* <Autocomplete
fullWidth
id="map-location-searcher"
freeSolo
@@ -2021,8 +1877,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
placeholder='Enter a location'
/>
)}
- /> */}
- {/* <EditableText
+ /> */
+}
+{
+ /* <EditableText
// editing
setVal={(newText: string | number) => typeof newText === 'string' && this.handleSearchChange(newText)}
// onEnter={e => this.bingSearch()}
@@ -2031,8 +1889,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// placeholder={this.bingSearchBarContents || 'Enter a location'}
placeholder='Enter a location'
textAlign="center"
- /> */}
- {/* <IconButton
+ /> */
+}
+{
+ /* <IconButton
icon={
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF">
<path
@@ -2045,4 +1905,5 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
/>
<div style={{ width: 30, height: 30 }} ref={this._dragRef} onPointerDown={this.dragToggle}>
<Button tooltip="drag to place a pushpin" icon={<FontAwesomeIcon size={'lg'} icon={'bullseye'} />} />
- </div> */}
+ </div> */
+}
diff --git a/src/client/views/nodes/MapBox/MapboxApiUtility.ts b/src/client/views/nodes/MapBox/MapboxApiUtility.ts
index 011b6f72a..592330ac2 100644
--- a/src/client/views/nodes/MapBox/MapboxApiUtility.ts
+++ b/src/client/views/nodes/MapBox/MapboxApiUtility.ts
@@ -34,48 +34,39 @@ export class MapboxApiUtility {
static getDirections = async (origin: number[], destination: number[]): Promise<Record<TransportationType, any> | undefined> => {
try {
- const drivingQuery = await fetch(
- `${MAPBOX_DIRECTIONS_BASE_URL}/driving/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`);
- const cyclingQuery = await fetch(
- `${MAPBOX_DIRECTIONS_BASE_URL}/cycling/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`);
+ const directionsPromises: Promise<any>[] = [];
+ const transportationTypes: TransportationType[] = ['driving', 'cycling', 'walking'];
- const walkingQuery = await fetch(
- `${MAPBOX_DIRECTIONS_BASE_URL}/walking/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`);
+ transportationTypes.forEach((type) => {
+ directionsPromises.push(
+ fetch(
+ `${MAPBOX_DIRECTIONS_BASE_URL}/${type}/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`
+ ).then((response) => response.json())
+ );
+ });
- const drivingJson = await drivingQuery.json();
- const cyclingJson = await cyclingQuery.json();
- const walkingJson = await walkingQuery.json();
-
- console.log("Driving: ", drivingJson);
- console.log("Cycling: ", cyclingJson);
- console.log("Waling: ", walkingJson);
-
- const routeMap = {
- 'driving': drivingJson.routes[0],
- 'cycling': cyclingJson.routes[0],
- 'walking': walkingJson.routes[0]
- }
+ const results = await Promise.all(directionsPromises);
const routeInfoMap: Record<TransportationType, any> = {
'driving': {},
'cycling': {},
'walking': {},
- };
-
- Object.entries(routeMap).forEach(([key, routeData]) => {
- const transportationTypeKey = key as TransportationType;
- const geometry = routeData.geometry;
- const coordinates = geometry.coordinates;
-
- console.log(coordinates);
-
- routeInfoMap[transportationTypeKey] = {
+ };
+
+ transportationTypes.forEach((type, index) => {
+ const routeData = results[index].routes[0];
+ if (routeData) {
+ const geometry = routeData.geometry;
+ const coordinates = geometry.coordinates;
+
+ routeInfoMap[type] = {
duration: this.secondsToMinutesHours(routeData.duration),
distance: this.metersToMiles(routeData.distance),
- coordinates: coordinates
+ coordinates: coordinates,
+ };
}
- })
+ });
return routeInfoMap;
@@ -102,4 +93,47 @@ export class MapboxApiUtility {
return `${parseFloat((meters/1609.34).toFixed(2))} mi`;
}
-} \ No newline at end of file
+}
+
+// const drivingQuery = await fetch(
+// `${MAPBOX_DIRECTIONS_BASE_URL}/driving/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`);
+
+// const cyclingQuery = await fetch(
+// `${MAPBOX_DIRECTIONS_BASE_URL}/cycling/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`);
+
+// const walkingQuery = await fetch(
+// `${MAPBOX_DIRECTIONS_BASE_URL}/walking/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`);
+
+// const drivingJson = await drivingQuery.json();
+// const cyclingJson = await cyclingQuery.json();
+// const walkingJson = await walkingQuery.json();
+
+// console.log("Driving: ", drivingJson);
+// console.log("Cycling: ", cyclingJson);
+// console.log("Waling: ", walkingJson);
+
+// const routeMap = {
+// 'driving': drivingJson.routes[0],
+// 'cycling': cyclingJson.routes[0],
+// 'walking': walkingJson.routes[0]
+// }
+
+// const routeInfoMap: Record<TransportationType, any> = {
+// 'driving': {},
+// 'cycling': {},
+// 'walking': {},
+// };
+
+// Object.entries(routeMap).forEach(([key, routeData]) => {
+// const transportationTypeKey = key as TransportationType;
+// const geometry = routeData.geometry;
+// const coordinates = geometry.coordinates;
+
+// console.log(coordinates);
+
+// routeInfoMap[transportationTypeKey] = {
+// duration: this.secondsToMinutesHours(routeData.duration),
+// distance: this.metersToMiles(routeData.distance),
+// coordinates: coordinates
+// }
+// }) \ No newline at end of file