diff options
author | zaultavangar <zaul_tavangar@brown.edu> | 2023-12-16 14:15:56 -0500 |
---|---|---|
committer | zaultavangar <zaul_tavangar@brown.edu> | 2023-12-16 14:15:56 -0500 |
commit | ddf35f6b406a2f2e8c27c2c65d15206eaa3ddbe6 (patch) | |
tree | 1c903bfbea31dc60a7ea019f933ade0d7d01afc2 | |
parent | 1dd4bbb41fedc6e0572cc1ead0dfc16db95d717a (diff) |
starting calendar feature
-rw-r--r-- | package-lock.json | 262 | ||||
-rw-r--r-- | package.json | 5 | ||||
-rw-r--r-- | src/client/util/CalendarManager.scss | 62 | ||||
-rw-r--r-- | src/client/util/CalendarManager.tsx | 229 | ||||
-rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 21 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox.scss | 3 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox.tsx | 42 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapboxApiUtility.ts | 98 |
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 |