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.tsx42
-rw-r--r--src/client/views/nodes/MapBox/MapboxApiUtility.ts98
9 files changed, 674 insertions, 50 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 f4526c490..25299532a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -895,19 +895,21 @@ 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();
@@ -981,6 +983,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
handleMapClick = (e: MapLayerMouseEvent) => {
+ this.featuresFromGeocodeResults = [];
if (this._mapRef.current){
const features = this._mapRef.current.queryRenderedFeatures(
e.point, {
@@ -1179,6 +1182,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (routeDoc){
MapAnchorMenu.Instance.fadeOut(true);
document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
+ this.featuresFromGeocodeResults = [];
this.routeToAnimate = routeDoc;
}
}
@@ -1675,6 +1679,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}), MapBox._rerenderDelay);
return null;
}
+ const scale = this.props.NativeDimScaling?.() || 1;
const renderAnnotations = (childFilters?: () => string[]) => null;
return (
@@ -1686,11 +1691,12 @@ 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 &&
- <div className="mapBox-searchbar">
+ <div className="mapBox-searchbar" style={{ zIndex: 1, position: 'relative', background: 'lightGray' }}>
<TextField
fullWidth
placeholder='Enter a location'
@@ -1755,7 +1761,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</div>
}
{this.routeToAnimate &&
- <div className='animation-panel'>
+ <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>
@@ -1809,7 +1815,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
id="mapbox-map"
mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'}
- style={{height: NumCast(this.layoutDoc._height), width: NumCast(this.layoutDoc._width)}}
+ style={{
+ transformOrigin: 'center',
+ transform: `scale(${scale < 1 ? 1 : scale})`,
+ zIndex: '0',
+ width: '100%',
+ height: '100%',
+ }}
initialViewState={this.isAnimating ? undefined : this.mapboxMapViewState}
onMove={this.onMapMove}
onClick={this.handleMapClick}
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