aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2023-08-13 17:06:15 -0400
committerSophie Zhang <sophie_zhang@brown.edu>2023-08-13 17:06:15 -0400
commit7fe84ba95c30a082f5146e684d0c31b61ec676df (patch)
tree6621ae183ff7032a8d8fff097e7f68b2ad574a03
parent314f62bb5335e3fb3f25501823d41cf0cdc53cac (diff)
parent3b45f1d30a947dc1702ec347b83e98374c5b603c (diff)
Merge branch 'master' into sophie-ai-images
-rw-r--r--package-lock.json507
-rw-r--r--package.json5
-rw-r--r--src/Utils.ts4
-rw-r--r--src/client/DocServer.ts61
-rw-r--r--src/client/documents/DocumentTypes.ts4
-rw-r--r--src/client/documents/Documents.ts44
-rw-r--r--src/client/util/CurrentUserUtils.ts174
-rw-r--r--src/client/util/DocumentManager.ts5
-rw-r--r--src/client/util/DragManager.ts4
-rw-r--r--src/client/util/GroupManager.tsx31
-rw-r--r--src/client/util/GroupMemberView.tsx105
-rw-r--r--src/client/util/LinkFollower.ts14
-rw-r--r--src/client/util/LinkManager.ts4
-rw-r--r--src/client/util/PingManager.ts6
-rw-r--r--src/client/util/RTFMarkup.tsx2
-rw-r--r--src/client/util/ReplayMovements.ts1
-rw-r--r--src/client/util/SelectionManager.ts3
-rw-r--r--src/client/util/ServerStats.tsx26
-rw-r--r--src/client/util/SettingsManager.tsx11
-rw-r--r--src/client/util/SharingManager.tsx68
-rw-r--r--src/client/util/reportManager/ReportManager.tsx5
-rw-r--r--src/client/views/AudioWaveform.tsx6
-rw-r--r--src/client/views/DashboardView.tsx83
-rw-r--r--src/client/views/DocComponent.tsx6
-rw-r--r--src/client/views/DocumentButtonBar.tsx1
-rw-r--r--src/client/views/DocumentDecorations.tsx14
-rw-r--r--src/client/views/EditableView.tsx3
-rw-r--r--src/client/views/FilterPanel.tsx18
-rw-r--r--src/client/views/InkTranscription.tsx2
-rw-r--r--src/client/views/LightboxView.tsx4
-rw-r--r--src/client/views/Main.tsx11
-rw-r--r--src/client/views/MainView.tsx17
-rw-r--r--src/client/views/PropertiesButtons.tsx5
-rw-r--r--src/client/views/PropertiesDocContextSelector.tsx1
-rw-r--r--src/client/views/PropertiesSection.tsx80
-rw-r--r--src/client/views/PropertiesView.tsx758
-rw-r--r--src/client/views/StyleProvider.tsx12
-rw-r--r--src/client/views/UndoStack.tsx43
-rw-r--r--src/client/views/animationtimeline/Keyframe.tsx10
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx2
-rw-r--r--src/client/views/animationtimeline/Track.tsx42
-rw-r--r--src/client/views/collections/CollectionDockingView.scss1
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx7
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx41
-rw-r--r--src/client/views/collections/CollectionMenu.tsx37
-rw-r--r--src/client/views/collections/CollectionPileView.tsx3
-rw-r--r--src/client/views/collections/CollectionStackingView.scss1
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx32
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx1
-rw-r--r--src/client/views/collections/TabDocView.tsx2
-rw-r--r--src/client/views/collections/TreeView.tsx78
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx28
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx1
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx33
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx1
-rw-r--r--src/client/views/global/globalScripts.ts10
-rw-r--r--src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx79
-rw-r--r--src/client/views/newlightbox/ExploreView/ExploreView.tsx50
-rw-r--r--src/client/views/newlightbox/components/Recommendation/Recommendation.tsx170
-rw-r--r--src/client/views/nodes/DataVizBox/utils/D3Utils.ts1
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.tsx72
-rw-r--r--src/client/views/nodes/EquationBox.scss9
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx241
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx1
-rw-r--r--src/client/views/nodes/ScreenshotBox.scss36
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx12
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx14
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss20
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx68
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts26
-rw-r--r--src/client/views/nodes/importBox/ImportElementBox.tsx88
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx72
-rw-r--r--src/client/views/search/SearchBox.tsx79
-rw-r--r--src/client/views/topbar/TopBar.tsx44
-rw-r--r--src/fields/Doc.ts85
-rw-r--r--src/fields/DocSymbols.ts2
-rw-r--r--src/fields/FieldLoader.tsx5
-rw-r--r--src/fields/List.ts46
-rw-r--r--src/fields/Types.ts9
-rw-r--r--src/fields/URLField.ts2
-rw-r--r--src/mobile/MobileMain.tsx2
-rw-r--r--src/server/ApiManagers/UploadManager.ts3
-rw-r--r--src/server/ApiManagers/UserManager.ts16
-rw-r--r--src/server/DashUploadUtils.ts34
-rw-r--r--src/server/server_Initialization.ts4
89 files changed, 1967 insertions, 1815 deletions
diff --git a/package-lock.json b/package-lock.json
index 3e93d4c20..658e783f9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4,6 +4,15 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+ "@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
"@azure/abort-controller": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
@@ -120,20 +129,6 @@
}
},
"@babel/code-frame": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
- "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
- "@ampproject/remapping": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
- "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg=="
- },
- "requires": {
- "@jridgewell/gen-mapping": "^0.3.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- },
- "@babel/code-frame": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
@@ -147,20 +142,20 @@
"integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ=="
},
"@babel/core": {
- "version": "7.22.9",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz",
- "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz",
+ "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==",
"requires": {
"@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.22.5",
- "@babel/generator": "^7.22.9",
- "@babel/helper-compilation-targets": "^7.22.9",
+ "@babel/code-frame": "^7.22.10",
+ "@babel/generator": "^7.22.10",
+ "@babel/helper-compilation-targets": "^7.22.10",
"@babel/helper-module-transforms": "^7.22.9",
- "@babel/helpers": "^7.22.6",
- "@babel/parser": "^7.22.7",
+ "@babel/helpers": "^7.22.10",
+ "@babel/parser": "^7.22.10",
"@babel/template": "^7.22.5",
- "@babel/traverse": "^7.22.8",
- "@babel/types": "^7.22.5",
+ "@babel/traverse": "^7.22.10",
+ "@babel/types": "^7.22.10",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -168,17 +163,68 @@
"semver": "^6.3.1"
},
"dependencies": {
+ "@babel/code-frame": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz",
+ "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==",
+ "requires": {
+ "@babel/highlight": "^7.22.10",
+ "chalk": "^2.4.2"
+ }
+ },
"@babel/generator": {
- "version": "7.22.9",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz",
- "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz",
+ "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==",
"requires": {
- "@babel/types": "^7.22.5",
+ "@babel/types": "^7.22.10",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
}
},
+ "@babel/highlight": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz",
+ "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==",
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.22.5",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz",
+ "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ=="
+ },
+ "@babel/traverse": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz",
+ "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==",
+ "requires": {
+ "@babel/code-frame": "^7.22.10",
+ "@babel/generator": "^7.22.10",
+ "@babel/helper-environment-visitor": "^7.22.5",
+ "@babel/helper-function-name": "^7.22.5",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.22.10",
+ "@babel/types": "^7.22.10",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
+ "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "requires": {
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.5",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -224,17 +270,29 @@
}
},
"@babel/helper-builder-binary-assignment-operator-visitor": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz",
- "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz",
+ "integrity": "sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==",
"requires": {
- "@babel/types": "^7.22.5"
+ "@babel/types": "^7.22.10"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
+ "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "requires": {
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.5",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
}
},
"@babel/helper-compilation-targets": {
- "version": "7.22.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz",
- "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz",
+ "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==",
"requires": {
"@babel/compat-data": "^7.22.9",
"@babel/helper-validator-option": "^7.22.5",
@@ -264,9 +322,9 @@
}
},
"@babel/helper-create-class-features-plugin": {
- "version": "7.22.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz",
- "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz",
+ "integrity": "sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==",
"requires": {
"@babel/helper-annotate-as-pure": "^7.22.5",
"@babel/helper-environment-visitor": "^7.22.5",
@@ -489,23 +547,112 @@
"integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw=="
},
"@babel/helper-wrap-function": {
- "version": "7.22.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz",
- "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz",
+ "integrity": "sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==",
"requires": {
"@babel/helper-function-name": "^7.22.5",
"@babel/template": "^7.22.5",
- "@babel/types": "^7.22.5"
+ "@babel/types": "^7.22.10"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
+ "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "requires": {
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.5",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
}
},
"@babel/helpers": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz",
- "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz",
+ "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==",
"requires": {
"@babel/template": "^7.22.5",
- "@babel/traverse": "^7.22.6",
- "@babel/types": "^7.22.5"
+ "@babel/traverse": "^7.22.10",
+ "@babel/types": "^7.22.10"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz",
+ "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==",
+ "requires": {
+ "@babel/highlight": "^7.22.10",
+ "chalk": "^2.4.2"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz",
+ "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==",
+ "requires": {
+ "@babel/types": "^7.22.10",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz",
+ "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==",
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.22.5",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz",
+ "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ=="
+ },
+ "@babel/traverse": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz",
+ "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==",
+ "requires": {
+ "@babel/code-frame": "^7.22.10",
+ "@babel/generator": "^7.22.10",
+ "@babel/helper-environment-visitor": "^7.22.5",
+ "@babel/helper-function-name": "^7.22.5",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.22.10",
+ "@babel/types": "^7.22.10",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
+ "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "requires": {
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.5",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
}
},
"@babel/highlight": {
@@ -596,15 +743,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="
},
- "@babel/plugin-proposal-unicode-property-regex": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
- "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
"@babel/plugin-syntax-async-generators": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
@@ -803,13 +941,13 @@
}
},
"@babel/plugin-transform-async-generator-functions": {
- "version": "7.22.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz",
- "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz",
+ "integrity": "sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==",
"requires": {
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-remap-async-to-generator": "^7.22.5",
+ "@babel/helper-remap-async-to-generator": "^7.22.9",
"@babel/plugin-syntax-async-generators": "^7.8.4"
},
"dependencies": {
@@ -853,9 +991,9 @@
}
},
"@babel/plugin-transform-block-scoping": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz",
- "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz",
+ "integrity": "sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5"
},
@@ -948,9 +1086,9 @@
}
},
"@babel/plugin-transform-destructuring": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz",
- "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz",
+ "integrity": "sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5"
},
@@ -1317,9 +1455,9 @@
}
},
"@babel/plugin-transform-optional-chaining": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz",
- "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz",
+ "integrity": "sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
@@ -1488,12 +1626,12 @@
}
},
"@babel/plugin-transform-regenerator": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz",
- "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz",
+ "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
- "regenerator-transform": "^0.15.1"
+ "regenerator-transform": "^0.15.2"
},
"dependencies": {
"@babel/helper-plugin-utils": {
@@ -1595,12 +1733,12 @@
}
},
"@babel/plugin-transform-typescript": {
- "version": "7.22.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz",
- "integrity": "sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.10.tgz",
+ "integrity": "sha512-7++c8I/ymsDo4QQBAgbraXLzIM6jmfao11KgIBEYZRReWzNWH9NtNgJcyrZiXsOPh523FQm6LfpLyy/U5fn46A==",
"requires": {
"@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-create-class-features-plugin": "^7.22.9",
+ "@babel/helper-create-class-features-plugin": "^7.22.10",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-typescript": "^7.22.5"
},
@@ -1621,9 +1759,9 @@
}
},
"@babel/plugin-transform-unicode-escapes": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz",
- "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz",
+ "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5"
},
@@ -1684,12 +1822,12 @@
}
},
"@babel/preset-env": {
- "version": "7.22.9",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz",
- "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.10.tgz",
+ "integrity": "sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A==",
"requires": {
"@babel/compat-data": "^7.22.9",
- "@babel/helper-compilation-targets": "^7.22.9",
+ "@babel/helper-compilation-targets": "^7.22.10",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-validator-option": "^7.22.5",
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5",
@@ -1714,15 +1852,15 @@
"@babel/plugin-syntax-top-level-await": "^7.14.5",
"@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
"@babel/plugin-transform-arrow-functions": "^7.22.5",
- "@babel/plugin-transform-async-generator-functions": "^7.22.7",
+ "@babel/plugin-transform-async-generator-functions": "^7.22.10",
"@babel/plugin-transform-async-to-generator": "^7.22.5",
"@babel/plugin-transform-block-scoped-functions": "^7.22.5",
- "@babel/plugin-transform-block-scoping": "^7.22.5",
+ "@babel/plugin-transform-block-scoping": "^7.22.10",
"@babel/plugin-transform-class-properties": "^7.22.5",
"@babel/plugin-transform-class-static-block": "^7.22.5",
"@babel/plugin-transform-classes": "^7.22.6",
"@babel/plugin-transform-computed-properties": "^7.22.5",
- "@babel/plugin-transform-destructuring": "^7.22.5",
+ "@babel/plugin-transform-destructuring": "^7.22.10",
"@babel/plugin-transform-dotall-regex": "^7.22.5",
"@babel/plugin-transform-duplicate-keys": "^7.22.5",
"@babel/plugin-transform-dynamic-import": "^7.22.5",
@@ -1745,27 +1883,27 @@
"@babel/plugin-transform-object-rest-spread": "^7.22.5",
"@babel/plugin-transform-object-super": "^7.22.5",
"@babel/plugin-transform-optional-catch-binding": "^7.22.5",
- "@babel/plugin-transform-optional-chaining": "^7.22.6",
+ "@babel/plugin-transform-optional-chaining": "^7.22.10",
"@babel/plugin-transform-parameters": "^7.22.5",
"@babel/plugin-transform-private-methods": "^7.22.5",
"@babel/plugin-transform-private-property-in-object": "^7.22.5",
"@babel/plugin-transform-property-literals": "^7.22.5",
- "@babel/plugin-transform-regenerator": "^7.22.5",
+ "@babel/plugin-transform-regenerator": "^7.22.10",
"@babel/plugin-transform-reserved-words": "^7.22.5",
"@babel/plugin-transform-shorthand-properties": "^7.22.5",
"@babel/plugin-transform-spread": "^7.22.5",
"@babel/plugin-transform-sticky-regex": "^7.22.5",
"@babel/plugin-transform-template-literals": "^7.22.5",
"@babel/plugin-transform-typeof-symbol": "^7.22.5",
- "@babel/plugin-transform-unicode-escapes": "^7.22.5",
+ "@babel/plugin-transform-unicode-escapes": "^7.22.10",
"@babel/plugin-transform-unicode-property-regex": "^7.22.5",
"@babel/plugin-transform-unicode-regex": "^7.22.5",
"@babel/plugin-transform-unicode-sets-regex": "^7.22.5",
- "@babel/preset-modules": "^0.1.5",
- "@babel/types": "^7.22.5",
- "babel-plugin-polyfill-corejs2": "^0.4.4",
- "babel-plugin-polyfill-corejs3": "^0.8.2",
- "babel-plugin-polyfill-regenerator": "^0.5.1",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "@babel/types": "^7.22.10",
+ "babel-plugin-polyfill-corejs2": "^0.4.5",
+ "babel-plugin-polyfill-corejs3": "^0.8.3",
+ "babel-plugin-polyfill-regenerator": "^0.5.2",
"core-js-compat": "^3.31.0",
"semver": "^6.3.1"
},
@@ -1775,6 +1913,16 @@
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
"integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg=="
},
+ "@babel/types": {
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
+ "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "requires": {
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.5",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
"semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -1783,13 +1931,11 @@
}
},
"@babel/preset-modules": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz",
- "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==",
+ "version": "0.1.6-no-external-plugins",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
- "@babel/plugin-transform-dotall-regex": "^7.4.4",
"@babel/types": "^7.4.4",
"esutils": "^2.0.2"
}
@@ -3065,12 +3211,17 @@
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
- "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
+ "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
"requires": {
- "regenerator-runtime": "^0.13.11"
+ "regenerator-runtime": "^0.14.0"
}
+ },
+ "regenerator-runtime": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
+ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
}
}
},
@@ -6356,9 +6507,9 @@
}
},
"browndash-components": {
- "version": "0.0.90",
- "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.90.tgz",
- "integrity": "sha512-56dsp2yoP5axTnXSPtSpWsQUkcj3hAekYBHLk7Kw6OV7LHVuu5PZruSmGE9xDrdhAFz2UdUyxKeAGB3PdbQoew==",
+ "version": "0.0.92",
+ "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.92.tgz",
+ "integrity": "sha512-eE/6WQNZiLnaXUKyoaMm0PDYjExUsFJ9VTAIIxROpYPosIBKWNZ743xaOfmehib5us9hEXJb0CvUFJQb8rzDVw==",
"requires": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
@@ -6373,9 +6524,9 @@
},
"dependencies": {
"@babel/cli": {
- "version": "7.22.9",
- "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.22.9.tgz",
- "integrity": "sha512-nb2O7AThqRo7/E53EGiuAkMaRbb7J5Qp3RvN+dmua1U+kydm0oznkhqbTEG15yk26G/C3yL6OdZjzgl+DMXVVA==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.22.10.tgz",
+ "integrity": "sha512-rM9ZMmaII630zGvtMtQ3P4GyHs28CHLYE9apLG7L8TgaSqcfoIGrlLSLsh4Q8kDTdZQQEXZm1M0nQtOvU/2heg==",
"requires": {
"@jridgewell/trace-mapping": "^0.3.17",
"@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
@@ -6476,48 +6627,48 @@
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
},
"@mui/base": {
- "version": "5.0.0-beta.8",
- "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.8.tgz",
- "integrity": "sha512-b4vVjMZx5KzzEMf4arXKoeV5ZegAMOoPwoy1vfUBwhvXc2QtaaAyBp50U7OA2L06Leubc1A+lEp3eqwZoFn87g==",
+ "version": "5.0.0-beta.10",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.10.tgz",
+ "integrity": "sha512-moTAhGwFfQffj7hsu61FnqcGqVcd53A1CrOhnskM9TF0Uh2rnLDMCuar4JRUWWpaJofAfQEbQBBFPadFQLI4PA==",
"requires": {
"@babel/runtime": "^7.22.6",
"@emotion/is-prop-valid": "^1.2.1",
"@mui/types": "^7.2.4",
- "@mui/utils": "^5.14.1",
+ "@mui/utils": "^5.14.4",
"@popperjs/core": "^2.11.8",
- "clsx": "^1.2.1",
+ "clsx": "^2.0.0",
"prop-types": "^15.8.1",
"react-is": "^18.2.0"
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
- "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
+ "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
"requires": {
- "regenerator-runtime": "^0.13.11"
+ "regenerator-runtime": "^0.14.0"
}
}
}
},
"@mui/core-downloads-tracker": {
- "version": "5.14.2",
- "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.2.tgz",
- "integrity": "sha512-x+c/MgDL1t/IIy5lDbMlrDouFG5DYZbl3DP4dbbuhlpPFBnE9glYwmJEee/orVHQpOPwLxCAIWQs+2DKSaBVWQ=="
+ "version": "5.14.4",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.4.tgz",
+ "integrity": "sha512-pW2XghSi3hpYKX57Wu0SCWMTSpzvXZmmucj3TcOJWaCiFt4xr05w2gcwBZi36dAp9uvd9//9N51qbblmnD+GPg=="
},
"@mui/material": {
- "version": "5.14.2",
- "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.2.tgz",
- "integrity": "sha512-TgNR4/YRL11RifsnMWNhITNCkGJYVz20SCvVJBBoU5Y/KhUNSSJxjDpEB8VrnY+sUsV0NigLCkHZJglfsiS3Pw==",
+ "version": "5.14.4",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.4.tgz",
+ "integrity": "sha512-2XUV3KyRC07BQPPzEgd+ss3x/ezXtHeKtOGCMCNmx3MauZojPYUpSwFkE0fYgYCD9dMQMVG4DY/VF38P0KShsg==",
"requires": {
"@babel/runtime": "^7.22.6",
- "@mui/base": "5.0.0-beta.8",
- "@mui/core-downloads-tracker": "^5.14.2",
- "@mui/system": "^5.14.1",
+ "@mui/base": "5.0.0-beta.10",
+ "@mui/core-downloads-tracker": "^5.14.4",
+ "@mui/system": "^5.14.4",
"@mui/types": "^7.2.4",
- "@mui/utils": "^5.14.1",
+ "@mui/utils": "^5.14.4",
"@types/react-transition-group": "^4.4.6",
- "clsx": "^1.2.1",
+ "clsx": "^2.0.0",
"csstype": "^3.1.2",
"prop-types": "^15.8.1",
"react-is": "^18.2.0",
@@ -6525,31 +6676,31 @@
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
- "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
+ "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
"requires": {
- "regenerator-runtime": "^0.13.11"
+ "regenerator-runtime": "^0.14.0"
}
}
}
},
"@mui/private-theming": {
- "version": "5.13.7",
- "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.13.7.tgz",
- "integrity": "sha512-qbSr+udcij5F9dKhGX7fEdx2drXchq7htLNr2Qg2Ma+WJ6q0ERlEqGSBiPiVDJkptcjeVL4DGmcf1wl5+vD4EA==",
+ "version": "5.14.4",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.4.tgz",
+ "integrity": "sha512-ISXsHDiQ3z1XA4IuKn+iXDWvDjcz/UcQBiFZqtdoIsEBt8CB7wgdQf3LwcwqO81dl5ofg/vNQBEnXuKfZHrnYA==",
"requires": {
- "@babel/runtime": "^7.22.5",
- "@mui/utils": "^5.13.7",
+ "@babel/runtime": "^7.22.6",
+ "@mui/utils": "^5.14.4",
"prop-types": "^15.8.1"
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
- "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
+ "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
"requires": {
- "regenerator-runtime": "^0.13.11"
+ "regenerator-runtime": "^0.14.0"
}
}
}
@@ -6566,44 +6717,44 @@
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
- "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
+ "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
"requires": {
- "regenerator-runtime": "^0.13.11"
+ "regenerator-runtime": "^0.14.0"
}
}
}
},
"@mui/system": {
- "version": "5.14.1",
- "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.1.tgz",
- "integrity": "sha512-u+xlsU34Jdkgx1CxmBnIC4Y08uPdVX5iEd3S/1dggDFtOGp+Lj8xmKRJAQ8PJOOJLOh8pDwaZx4AwXikL4l1QA==",
+ "version": "5.14.4",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.4.tgz",
+ "integrity": "sha512-oPgfWS97QNfHcDBapdkZIs4G5i85BJt69Hp6wbXF6s7vi3Evcmhdk8AbCRW6n0sX4vTj8oe0mh0RIm1G2A1KDA==",
"requires": {
"@babel/runtime": "^7.22.6",
- "@mui/private-theming": "^5.13.7",
+ "@mui/private-theming": "^5.14.4",
"@mui/styled-engine": "^5.13.2",
"@mui/types": "^7.2.4",
- "@mui/utils": "^5.14.1",
- "clsx": "^1.2.1",
+ "@mui/utils": "^5.14.4",
+ "clsx": "^2.0.0",
"csstype": "^3.1.2",
"prop-types": "^15.8.1"
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
- "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
+ "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
"requires": {
- "regenerator-runtime": "^0.13.11"
+ "regenerator-runtime": "^0.14.0"
}
}
}
},
"@mui/utils": {
- "version": "5.14.1",
- "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.1.tgz",
- "integrity": "sha512-39KHKK2JeqRmuUcLDLwM+c2XfVC136C5/yUyQXmO2PVbOb2Bol4KxtkssEqCbTwg87PSCG3f1Tb0keRsK7cVGw==",
+ "version": "5.14.4",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.4.tgz",
+ "integrity": "sha512-4ANV0txPD3x0IcTCSEHKDWnsutg1K3m6Vz5IckkbLXVYu17oOZCVUdOKsb/txUmaCd0v0PmSRe5PW+Mlvns5dQ==",
"requires": {
"@babel/runtime": "^7.22.6",
"@types/prop-types": "^15.7.5",
@@ -6613,11 +6764,11 @@
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
- "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
+ "version": "7.22.10",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
+ "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
"requires": {
- "regenerator-runtime": "^0.13.11"
+ "regenerator-runtime": "^0.14.0"
}
}
}
@@ -6686,6 +6837,11 @@
"readdirp": "~3.6.0"
}
},
+ "clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
+ },
"color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -8659,15 +8815,20 @@
"picomatch": "^2.2.1"
}
},
+ "regenerator-runtime": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
+ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
+ },
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="
},
"styled-components": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.5.tgz",
- "integrity": "sha512-308zi5o7LrA9cVaP4nPD0TaUpOjGPePkAUFb/OGB0xRI3I9ozpW5UyASvRVi9wJcYASG+Y3mLDLDUZC7nqzimw==",
+ "version": "6.0.7",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.7.tgz",
+ "integrity": "sha512-xIwWuiRMYR43mskVsW9MGTRjSo7ol4bcVjT595fGUp3OLBJOlOgaiKaxsHdC4a2HqWKqKnh0CmcRbk5ogyDjTg==",
"requires": {
"@babel/cli": "^7.21.0",
"@babel/core": "^7.21.0",
@@ -9785,9 +9946,9 @@
"integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw=="
},
"core-js-compat": {
- "version": "3.31.1",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz",
- "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==",
+ "version": "3.32.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.0.tgz",
+ "integrity": "sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw==",
"requires": {
"browserslist": "^4.21.9"
}
@@ -24611,9 +24772,9 @@
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"regenerator-transform": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz",
- "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==",
+ "version": "0.15.2",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
+ "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
"requires": {
"@babel/runtime": "^7.8.4"
}
diff --git a/package.json b/package.json
index 6204b5c13..2b26a2bbe 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,8 @@
"child_process": "empty"
},
"scripts": {
- "start-release": "cross-env RELEASE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts",
+ "start-release": "cross-env RELEASE=true USE_AZURE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts",
+ "start-release-debug": "cross-env RELEASE=true USE_AZURE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --inspect -- src/server/index.ts",
"start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts",
"oldstart": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug -- src/server/index.ts",
"debug": "cross-env USE_AZURE=false NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --transpile-only --inspect -- src/server/index.ts",
@@ -177,7 +178,7 @@
"body-parser": "^1.19.2",
"bootstrap": "^4.6.1",
"brotli": "^1.3.3",
- "browndash-components": "^0.0.90",
+ "browndash-components": "^0.0.93",
"browser-assert": "^1.2.1",
"bson": "^4.6.1",
"canvas": "^2.9.3",
diff --git a/src/Utils.ts b/src/Utils.ts
index 599c6456a..7f83ab8f5 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -76,11 +76,11 @@ export namespace Utils {
});
return returnedUri;
} catch (e) {
- console.log('VideoBox :' + e);
+ console.log('ConvertDataURI :' + e);
}
}
- export function GetScreenTransform(ele?: HTMLElement): { scale: number; translateX: number; translateY: number } {
+ export function GetScreenTransform(ele?: HTMLElement | null): { scale: number; translateX: number; translateY: number } {
if (!ele) {
return { scale: 1, translateX: 1, translateY: 1 };
}
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index ba59a9f50..53c7b857a 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,16 +1,18 @@
import { runInAction } from 'mobx';
import * as rp from 'request-promise';
import * as io from 'socket.io-client';
-import { Doc, Opt } from '../fields/Doc';
+import { Doc, DocListCast, Opt } from '../fields/Doc';
import { UpdatingFromServer } from '../fields/DocSymbols';
import { FieldLoader } from '../fields/FieldLoader';
import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols';
import { ObjectField } from '../fields/ObjectField';
import { RefField } from '../fields/RefField';
-import { StrCast } from '../fields/Types';
+import { DocCast, StrCast } from '../fields/Types';
import MobileInkOverlay from '../mobile/MobileInkOverlay';
import { emptyFunction, Utils } from '../Utils';
import { GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message';
+import { DocumentType } from './documents/DocumentTypes';
+import { LinkManager } from './util/LinkManager';
import { SerializationHelper } from './util/SerializationHelper';
import { GestureOverlay } from './views/GestureOverlay';
@@ -30,34 +32,44 @@ import { GestureOverlay } from './views/GestureOverlay';
export namespace DocServer {
let _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
- export function QUERY_SERVER_CACHE(title: string) {
+ export function FindDocByTitle(title: string) {
const foundDocId = Array.from(Object.keys(_cache))
.filter(key => _cache[key] instanceof Doc)
.find(key => (_cache[key] as Doc).title === title);
return foundDocId ? (_cache[foundDocId] as Doc) : undefined;
}
- let lastCacheUpdate = 0;
- export function UPDATE_SERVER_CACHE(print: boolean = false) {
- if (print) {
- const strings: string[] = [];
- Array.from(Object.keys(_cache)).forEach(key => {
- const doc = _cache[key];
- if (doc instanceof Doc) strings.push(StrCast(doc.author) + ' ' + StrCast(doc.title) + ' ' + StrCast(Doc.GetT(doc, 'title', 'string', true)));
- });
- strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
- }
- const filtered = Array.from(Object.keys(_cache)).filter(key => {
- const doc = _cache[key] as Doc;
- if (!(StrCast(doc.author).includes('.edu') || StrCast(doc.author).includes('.com')) || doc.author == Doc.CurrentUserEmail) return true;
- return false;
+ let cacheDocumentIds = ''; // ; separate string of all documents ids in the user's working set (cached on the server)
+ export let CacheNeedsUpdate = false;
+ export function UPDATE_SERVER_CACHE() {
+ const prototypes = Object.values(DocumentType)
+ .filter(type => type !== DocumentType.NONE)
+ .map(type => _cache[type + 'Proto'])
+ .filter(doc => doc instanceof Doc)
+ .map(doc => doc as Doc);
+ const references = new Set<Doc>(prototypes);
+ Doc.FindReferences(Doc.UserDoc(), references, undefined);
+ DocListCast(DocCast(Doc.UserDoc().myLinkDatabase).data).forEach(link => {
+ if (!references.has(DocCast(link.link_anchor_1)) && !references.has(DocCast(link.link_anchor_2))) {
+ Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase), 'data', link);
+ }
});
- if (filtered.length === lastCacheUpdate) return;
- lastCacheUpdate = filtered.length;
+ LinkManager.userLinkDBs.forEach(linkDb => Doc.FindReferences(linkDb, references, undefined));
+ const filtered = Array.from(references);
+
+ const newCacheUpdate = filtered.map(doc => doc[Id]).join(';');
+ if (newCacheUpdate === cacheDocumentIds) return;
+ cacheDocumentIds = newCacheUpdate;
+
+ // print out cached docs
+ console.log('Set cached docs = ');
+ const is_filtered = filtered.filter(doc => !Doc.IsSystem(doc));
+ const strings = is_filtered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
+ strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
rp.post(Utils.prepend('/setCacheDocumentIds'), {
body: {
- cacheDocumentIds: filtered.join(';'),
+ cacheDocumentIds,
},
json: true,
});
@@ -156,7 +168,7 @@ export namespace DocServer {
_cache = {};
USER_ID = identifier;
protocol = protocol.startsWith('https') ? 'wss' : 'ws';
- _socket = io.connect(`${protocol}://${hostname}:${port}`);
+ _socket = require('socket.io-client')(`${protocol}://${hostname}:${port}`, { transports: ['websocket'], rejectUnauthorized: false });
// io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket
_GetCachedRefField = _GetCachedRefFieldImpl;
@@ -352,7 +364,7 @@ export namespace DocServer {
// i) which documents need to be fetched
// ii) which are already in the process of being fetched
// iii) which already exist in the cache
- for (const id of ids) {
+ for (const id of ids.filter(id => id)) {
const cached = _cache[id];
if (cached === undefined) {
defaultPromises.push({
@@ -381,7 +393,7 @@ export namespace DocServer {
// fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
// the fields have been returned from the server
console.log('Requesting ' + requestedIds.length);
- FieldLoader.active && runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length));
+ setTimeout(() => runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length)));
const serializedFields = await Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
// 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
@@ -391,7 +403,7 @@ export namespace DocServer {
console.log('deserializing ' + serializedFields.length + ' fields');
for (const field of serializedFields) {
processed++;
- if (FieldLoader.active && processed % 150 === 0) {
+ if (processed % 150 === 0) {
runInAction(() => (FieldLoader.ServerLoadStatus.retrieved = processed));
await new Promise(res => setTimeout(res)); // force loading to yield to splash screen rendering to update progress
}
@@ -477,6 +489,7 @@ export namespace DocServer {
* @param field the [RefField] to be serialized and sent to the server to be stored in the database
*/
export function CreateField(field: RefField) {
+ CacheNeedsUpdate = true;
_CreateField(field);
}
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index b629d9b8c..1d0ddce40 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -14,13 +14,11 @@ export enum DocumentType {
INK = 'ink',
SCREENSHOT = 'screenshot',
FONTICON = 'fonticonbox',
- FILTER = 'filter',
SEARCH = 'search', // search query
LABEL = 'label', // simple text label
BUTTON = 'button', // onClick button
WEBCAM = 'webcam', // webcam
CONFIG = 'config', // configuration document intended to specify a view layout configuration, but not be directly rendered (e.g., for saving the page# of a PDF, or view transform of a collection)
- DATE = 'date', // calendar view of a date
SCRIPTING = 'script', // script editor
EQUATION = 'equation', // equation editor
FUNCPLOT = 'funcplot', // function plotter
@@ -31,14 +29,12 @@ export enum DocumentType {
// special purpose wrappers that either take no data or are compositions of lower level types
LINK = 'link',
- LINKANCHOR = 'linkanchor',
IMPORT = 'import',
SLIDER = 'slider',
PRES = 'presentation',
PRESELEMENT = 'preselement',
COLOR = 'color',
YOUTUBE = 'youtube',
- SEARCHITEM = 'searchitem',
COMPARISON = 'comparison',
GROUP = 'group',
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f3f645ca2..8a4a82e6d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,5 +1,5 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { action, runInAction } from 'mobx';
+import { action, reaction, runInAction } from 'mobx';
import { basename } from 'path';
import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, Field, Opt, updateCachedAcls } from '../../fields/Doc';
@@ -354,6 +354,7 @@ export class DocumentOptions {
onDoubleClick?: ScriptField;
onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked
onChildDoubleClick?: ScriptField; // script given to children of a collection to execute when they are double clicked
+ onClickScriptDisable?: STRt = new StrInfo('"always" disable click script, "never" disable click script, or default');
defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
onPointerDown?: ScriptField;
@@ -620,7 +621,7 @@ export namespace Docs {
DocumentType.WEBCAM,
{
layout: { view: RecordingBox, dataField: defaultDataKey },
- options: { systemIcon: 'BsFillCameraVideoFill' },
+ options: { systemIcon: 'BsFillCameraVideoFill' },
},
],
[
@@ -634,7 +635,7 @@ export namespace Docs {
DocumentType.CONFIG,
{
layout: { view: CollectionView, dataField: defaultDataKey },
- options: { layout_hideLinkButton: true, pointerEvents: 'none', layout_unrendered: true },
+ options: { layout_hideLinkButton: true, layout_unrendered: true },
},
],
[
@@ -693,7 +694,22 @@ export namespace Docs {
{
data: '',
layout: { view: PhysicsSimulationBox, dataField: defaultDataKey, _width: 1000, _height: 800 },
- options: { _height: 100, layout_forceReflow: true, nativeHeightUnfrozen: true, mass1: '', mass2: '', nativeDimModifiable: true, position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: '', systemIcon: 'BsShareFill' },
+ options: {
+ _height: 100,
+ layout_forceReflow: true,
+ nativeHeightUnfrozen: true,
+ mass1: '',
+ mass2: '',
+ nativeDimModifiable: true,
+ position: '',
+ acceleration: '',
+ pendulum: '',
+ spring: '',
+ wedge: '',
+ simulation: '',
+ review: '',
+ systemIcon: 'BsShareFill',
+ },
},
],
]);
@@ -728,6 +744,10 @@ export namespace Docs {
// an entry dedicated to the given DocumentType)
target && PrototypeMap.set(type, target);
});
+ reaction(
+ () => (proto => StrCast(proto?.BROADCAST_MESSAGE))(DocServer.GetCachedRefField('rtfProto') as Doc),
+ msg => msg && alert(msg)
+ );
}
/**
@@ -837,7 +857,7 @@ export namespace Docs {
dataProps.author_date = new DateField();
if (fieldKey) {
dataProps[`${fieldKey}_modificationDate`] = new DateField();
- dataProps[fieldKey] = data;
+ dataProps[fieldKey] = options.data ?? data;
// so that the list of annotations is already initialised, prevents issues in addonly.
// without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
@@ -871,8 +891,6 @@ export namespace Docs {
DocUtils.MakeLinkToActiveAudio(() => viewDoc);
}
- Doc.AddFileOrphan(dataDoc);
-
updateCachedAcls(dataDoc);
updateCachedAcls(viewDoc);
@@ -1058,7 +1076,12 @@ export namespace Docs {
}
export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile }, id);
+ return InstanceFromProto(
+ Prototypes.get(DocumentType.COL),
+ new List(documents),
+ { backgroundColor: 'transparent', dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
+ id
+ );
}
export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1134,9 +1157,6 @@ export namespace Docs {
export function FontIconDocument(options?: DocumentOptions) {
return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) });
}
- export function FilterDocument(options?: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.FILTER), undefined, { ...(options || {}) });
- }
export function PresElementBoxDocument() {
return Prototypes.get(DocumentType.PRESELEMENT);
@@ -1664,7 +1684,6 @@ export namespace DocUtils {
newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size;
newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size;
newCollection._width = newCollection._height = size * 2;
- newCollection._jitterRotation = 10;
return newCollection;
}
}
@@ -1856,7 +1875,6 @@ export namespace DocUtils {
export function copyDragFactory(dragFactory: Doc) {
if (!dragFactory) return undefined;
const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
- ndoc && Doc.AddFileOrphan(Doc.GetProto(ndoc));
if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2e4fb0f1c..c9a5175bb 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,4 +1,4 @@
-import { reaction } from "mobx";
+import { observable, reaction, runInAction } from "mobx";
import * as rp from 'request-promise';
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
import { FieldLoader } from "../../fields/FieldLoader";
@@ -152,7 +152,7 @@ export class CurrentUserUtils {
{ noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
{ noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
const reqdNoteList = reqdTempOpts.map(opts => {
- const reqdOpts = {...opts, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
+ const reqdOpts = {...opts, isSystem:true, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
});
@@ -295,7 +295,7 @@ export class CurrentUserUtils {
{ toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, clickFactory: DocCast(doc.emptyNote)},
{ toolTip: "Tap or drag to create a flashcard", title: "Flashcard", icon: "id-card", dragFactory: doc.emptyFlashcard as Doc, clickFactory: DocCast(doc.emptyFlashcard)},
{ toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyEquation as Doc, clickFactory: DocCast(doc.emptyEquation)},
- { toolTip: "Tap or drag to create a physics simulation", title: "Simulation", icon: "atom", dragFactory: doc.emptySimulation as Doc, },
+ { toolTip: "Tap or drag to create a physics simulation", title: "Simulation", icon: "atom", dragFactory: doc.emptySimulation as Doc, funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a note board", title: "Notes", icon: "folder", dragFactory: doc.emptyNoteboard as Doc, clickFactory: DocCast(doc.emptyNoteboard)},
{ toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab)},
{ toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, clickFactory: DocCast(doc.emptyWebpage)},
@@ -303,18 +303,19 @@ export class CurrentUserUtils {
{ toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, clickFactory: DocCast(doc.emptyAudio), openFactoryLocation: OpenWhere.overlay},
{ toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, clickFactory: DocCast(doc.emptyMap)},
{ toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, clickFactory: DocCast(doc.emptyScreengrab), openFactoryLocation: OpenWhere.overlay},
- { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, clickFactory: DocCast(doc.emptyWebCam), openFactoryLocation: OpenWhere.overlay},
+ { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, clickFactory: DocCast(doc.emptyWebCam), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, clickFactory: DocCast(doc.emptyButton)},
- { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript)},
+ { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript), funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, clickFactory: DocCast(doc.emptyDataViz)},
- { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay},
- { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true },
- { toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '<ScriptingRepl />' as any, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
- { toolTip: "Toggle an UndoStack", title: "undostacker", icon: "calculator", clickFactory: "<UndoStack>" as any, openFactoryLocation: OpenWhere.overlay},
+ { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true , funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '<ScriptingRepl />' as any, openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
+ { toolTip: "Toggle an UndoStack", title: "undostacker", icon: "calculator", clickFactory: "<UndoStack>" as any, openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
].map(tuple => (
{ openFactoryLocation: OpenWhere.addRight,
scripts: { onClick: 'openDoc(copyDragFactory(this.clickFactory,this.openFactoryAsDelegate), this.openFactoryLocation)',
onDragStart: '{ return copyDragFactory(this.dragFactory,this.openFactoryAsDelegate); }'},
+ funcs: tuple.funcs,
...tuple, }))
}
@@ -323,7 +324,7 @@ export class CurrentUserUtils {
const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit,
- _width: 35, _height: 35, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true,
+ _width: 60, _height: 60, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true,
btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
};
return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
@@ -339,35 +340,35 @@ export class CurrentUserUtils {
}
/// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
- static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] {
+ static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}, hidden?: boolean}[] {
const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())";
const getActiveDashTrails = "Doc.ActiveDashboard?.myTrails";
return [
{ title: "Dashboards", toolTip: "Dashboards", target: this.setupDashboards(doc, "myDashboards"), ignoreClick: true, icon: "desktop", funcs: {hidden: "IsNoviceMode()"} },
- { title: "Search", toolTip: "Search ⌘F", target: this.setupSearcher(doc, "mySearcher"), ignoreClick: true, icon: "search", },
+ { title: "Search", toolTip: "Search ⌘F", target: this.setupSearcher(doc, "mySearcher"), ignoreClick: true, icon: "search", },
{ title: "Files", toolTip: "Files", target: this.setupFilesystem(doc, "myFilesystem"), ignoreClick: true, icon: "folder-open", },
- { title: "Tools", toolTip: "Tools", target: this.setupToolsBtnPanel(doc, "myTools"), ignoreClick: true, icon: "wrench", funcs: {hidden: "IsNoviceMode()"} },
- { title: "Imports", toolTip: "Imports ⌘I", target: this.setupImportSidebar(doc, "myImports"), ignoreClick: true, icon: "upload", },
- { title: "Closed", toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), ignoreClick: true, icon: "archive", },
+ { title: "Tools", toolTip: "Tools", target: this.setupToolsBtnPanel(doc, "myTools"), ignoreClick: true, icon: "wrench", },
+ { title: "Imports", toolTip: "Imports ⌘I", target: this.setupImportSidebar(doc, "myImports"), ignoreClick:false, icon: "upload", },
+ { title: "Closed", toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), ignoreClick: true, icon: "archive", hidden: true }, // this doc is hidden from the Sidebar, but it's still being used in MyFilesystem which ignores the hidden field
{ title: "Shared", toolTip: "Shared Docs", target: Doc.MySharedDocs, ignoreClick: true, icon: "users", funcs: {badgeValue: badgeValue}},
- { title: "Trails", toolTip: "Trails ⌘R", target: Doc.UserDoc(), ignoreClick: true, icon: "pres-trail", funcs: {target: getActiveDashTrails}},
+ { title: "Trails", toolTip: "Trails ⌘R", target: Doc.UserDoc(), ignoreClick: true, icon: "pres-trail", funcs: {target: getActiveDashTrails}},
{ title: "User Doc", toolTip: "User Doc", target: this.setupUserDocView(doc, "myUserDocView"), ignoreClick: true, icon: "address-card",funcs: {hidden: "IsNoviceMode()"} },
].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}}));
}
/// the empty panel that is filled with whichever left menu button's panel has been selected
static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
- DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
+ DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {title:"leftSidebarPanel", isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
}
/// Initializes the left sidebar menu buttons and the panels they open up
static setupLeftSidebarMenu(doc: Doc, field="myLeftSidebarMenu") {
this.setupLeftSidebarPanel(doc);
const myLeftSidebarMenu = DocCast(doc[field]);
- const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, toolTip, scripts, funcs }) => {
+ const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, toolTip, hidden, scripts, funcs }) => {
const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
const reqdBtnOpts:DocumentOptions = {
- title, icon, target, toolTip, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List<string>(['height', 'data_columnHeaders']), dontRegisterView: true,
+ title, icon, target, toolTip, hidden, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List<string>(['height', 'data_columnHeaders']), dontRegisterView: true,
_width: 60, _height: 60, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
};
return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
@@ -495,7 +496,7 @@ export class CurrentUserUtils {
const reqdOpts:DocumentOptions = {
title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
dropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeViewTruncateTitleWidth: 350, ignoreClick: true,
- layout_headerButton: newDashboardButton, childDragAction: "embed",
+ layout_headerButton: newDashboardButton, childDragAction: "none",
_layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
contextMenuLabels:new List<string>(contextMenuLabels),
contextMenuIcons:new List<string>(contextMenuIcons),
@@ -519,7 +520,6 @@ export class CurrentUserUtils {
/// initializes the left sidebar File system pane
static setupFilesystem(doc: Doc, field:string) {
var myFilesystem = DocCast(doc[field]);
- const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", undoIgnoreFields:new List<string>(['treeViewSortCriterion']), _dragOnlyWithinContainer: true, isSystem: true, isFolder: true });
const newFolder = `TreeView_addNewFolder()`;
const newFolderOpts: DocumentOptions = {
@@ -530,14 +530,15 @@ export class CurrentUserUtils {
const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true,
- title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: "proto", isSystem: true,
+ title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: 'add', isSystem: true,
isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
treeViewTruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
childContextMenuLabels: new List<string>(["Create new folder"]),
childContextMenuIcons: new List<string>(["plus"]),
layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
};
- myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
+ const fileFolders = new Set(DocListCast(DocCast(doc[field])?.data));
+ myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, Array.from(fileFolders));
const childContextMenuScripts = [newFolder];
if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
@@ -547,8 +548,8 @@ export class CurrentUserUtils {
/// initializes the panel displaying docs that have been recently closed
static setupRecentlyClosed(doc: Doc, field:string) {
- const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true,
- title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "embed", isSystem: true,
+ const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, isFolder: true,
+ title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "move", isSystem: true,
treeViewTruncateTitleWidth: 350, ignoreClick: true, layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
contextMenuLabels: new List<string>(["Empty recently closed"]),
contextMenuIcons:new List<string>(["trash"]),
@@ -562,8 +563,6 @@ export class CurrentUserUtils {
toolTip: "Empty recently closed",};
DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
- //if (recentlyClosed.layout_headerButton !== clearDocsButton) Doc.GetProto(recentlyClosed).layout_headerButton = clearDocsButton;
-
if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
}
@@ -586,6 +585,11 @@ export class CurrentUserUtils {
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
_lockedPosition: true, isSystem: true, flexDirection: "row"
})
+ static multiToggleList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.FontIconDocument({
+ ...opts, data:docs, _gridGap: 0, _xMargin: 5, _yMargin: 5, layout_boxShadow: "0 0", _forceActive: true,
+ dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+ _lockedPosition: true, isSystem: true, flexDirection: "row"
+ })
static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
btnType: ButtonType.ToolButton, _forceActive: true, _layout_hideContextMenu: true,
@@ -640,17 +644,17 @@ export class CurrentUserUtils {
btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
{ title: "Font Size",toolTip: "Font size (%size)", btnType: ButtonType.NumberDropdownButton, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 6 },
{ title: "Color", toolTip: "Font color (%color)", btnType: ButtonType.ColorButton, icon: "font", toolType:"fontColor",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}},
- { title: "Highlight",toolTip:"Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", toolType:"highlight",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
+ { title: "Highlight",toolTip: "Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", toolType:"highlight",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
{ title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", toolType:"bold", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
{ title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", toolType:"italics", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
{ title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", toolType:"underline",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
{ title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", toolType:"bullet", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
{ title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", toolType:"decimal", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
- { title: "Alignment",toolTip: "Alignment", btnType: ButtonType.MultiToggleButton, toolType:"alignment", ignoreClick: true,
+ { title: "Align", toolTip: "Alignment", btnType: ButtonType.MultiToggleButton, toolType:"alignment", ignoreClick: true,
subMenu: [
- { title: "Left", toolTip: "Left align (Cmd-[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }},
- { title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
- { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+ { title: "Left", toolTip: "Left align (Cmd-[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }},
+ { title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+ { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
]
},
{ title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}},
@@ -730,32 +734,30 @@ export class CurrentUserUtils {
return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
}
+ static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc {
+ const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title);
+ const subMenu = params.subMenu;
+ if (!subMenu) { // button does not have a sub menu
+ return this.setupContextMenuButton(params, menuBtnDoc);
+ }
+ // linear view
+ const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsOpen"]),
+ childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
+ linearView_SubMenu: true, linearView_Expandable: params.btnType !== ButtonType.MultiToggleButton};
+
+ const items = (menuBtnDoc?:Doc) => !menuBtnDoc ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menuBtnDoc) );
+ const creator = params.btnType === ButtonType.MultiToggleButton ? this.multiToggleList : this.linearButtonList;
+ const btnDoc = DocUtils.AssignScripts( DocUtils.AssignDocField(menuDoc, StrCast(params.title),
+ (opts) => creator(opts, items(menuBtnDoc)), reqdSubMenuOpts, items(menuBtnDoc)), params.scripts, params.funcs);
+ if (!menuBtnDoc) Doc.GetProto(btnDoc).data = new List<Doc>(items(btnDoc));
+ return btnDoc;
+ }
+
/// Initializes all the default buttons for the top bar context menu
static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true, linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
- const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
- const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
- if (!params.subMenu) { // button does not have a sub menu
- return this.setupContextMenuButton(params, menuBtnDoc);
- } else { // linear view
- let reqdSubMenuOpts;
- if (params.btnType === ButtonType.MultiToggleButton) {
- reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsOpen"]),
- childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
- linearView_SubMenu: true, linearView_Dropdown: true, };
- } else {
- reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsOpen"]),
- childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
- linearView_SubMenu: true, linearView_Expandable: true, };
- }
- const items = params.subMenu?.map(sub =>
- this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title))
- );
- return DocUtils.AssignScripts(
- DocUtils.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), params.scripts, params.funcs);
- }
- });
+ const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => this.setupContextMenuBtn(params, ctxtMenuBtnsDoc) );
return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
}
@@ -774,6 +776,7 @@ export class CurrentUserUtils {
const linkDocs = new Doc(linkDatabaseId, true);
linkDocs.title = "LINK DATABASE: " + Doc.CurrentUserEmail;
linkDocs.author = Doc.CurrentUserEmail;
+ linkDocs.isSystem = true;
linkDocs.data = new List<Doc>([]);
linkDocs["acl-Guest"] = SharingPermissions.Augment;
doc.myLinkDatabase = new PrefetchProxy(linkDocs);
@@ -805,19 +808,17 @@ export class CurrentUserUtils {
DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
if (!Doc.GetProto(DocCast(doc.mySharedDocs)).data_dashboards) Doc.GetProto(DocCast(doc.mySharedDocs)).data_dashboards = new List<Doc>();
- console.log(doc.mySharedDocs);
}
/// Import option on the left side button panel
- static setupImportSidebar(doc: Doc, field:string) {
- // PresElementBox.LayoutString('data')
+ static setupImportSidebar(doc: Doc, field:string) {
const reqdOpts:DocumentOptions = {
- title: "My Imports", _forceActive: true, ignoreClick: true, _layout_showTitle: "title", childLayoutString: ImportElementBox.LayoutString('data'),
- _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0,
+ title: "My Imports", _forceActive: true, _layout_showTitle: "title", childLayoutString: ImportElementBox.LayoutString('data'),
+ _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0, onClickScriptDisable:"never",
childDragAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go."
};
- const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
+ const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.MasonryDocument([], opts), reqdOpts, undefined, {onClick: "deselectAll()"});
const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
_width: 30, _height: 30, color: Colors.BLACK, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton,
@@ -876,11 +877,13 @@ export class CurrentUserUtils {
this.setupDockedButtons(doc); // the bottom bar of font icons
this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
this.setupDocTemplates(doc); // sets up the template menu of templates
- this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
+ //this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {});
- DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
+ DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
+ Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards)
Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
+ Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed)
if (doc.activeDashboard instanceof Doc) {
// undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
@@ -888,7 +891,7 @@ export class CurrentUserUtils {
}
new LinkManager();
- setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
+ DocServer.CacheNeedsUpdate && setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
setInterval(DocServer.UPDATE_SERVER_CACHE, 120000);
return doc;
}
@@ -911,22 +914,22 @@ export class CurrentUserUtils {
});
}
+ @observable public static ServerVersion: string = ';'
public static async loadCurrentUser() {
return rp.get(Utils.prepend("/getCurrentUser")).then(async response => {
if (response) {
- const result: { id: string, email: string, cacheDocumentIds: string } = JSON.parse(response);
+ const result: { version: string, userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response);
+ runInAction(() => CurrentUserUtils.ServerVersion = result.version);
Doc.CurrentUserEmail = result.email;
- resolvedPorts = JSON.parse(await (await fetch("/resolvedPorts")).text());
+ resolvedPorts = result.resolvedPorts as any;
DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email);
if (result.cacheDocumentIds)
{
const ids = result.cacheDocumentIds.split(";");
const batch = 30000;
- FieldLoader.active = true;
for (let i = 0; i < ids.length; i = Math.min(ids.length, i+batch)) {
await DocServer.GetRefFields(ids.slice(i, i+batch));
}
- FieldLoader.active = false;
}
return result;
} else {
@@ -935,27 +938,24 @@ export class CurrentUserUtils {
});
}
- public static async loadUserDocument(id: string) {
- await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
- const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids);
- if (userDocumentId) {
- return DocServer.GetRefField(userDocumentId).then(async field => {
- Docs.newAccount = !(field instanceof Doc);
- await Docs.Prototypes.initialize();
- const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
- this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
- if (Docs.newAccount) {
- if (Doc.CurrentUserEmail === "guest") {
- DashboardView.createNewDashboard(undefined, "guest dashboard");
- } else {
- userDoc.activePage = "home";
- }
- }
- return userDoc;
- });
- } else {
- throw new Error("There should be a user id! Why does Dash think there isn't one?");
+ public static async loadUserDocument(info:{
+ userDocumentId: string;
+ sharingDocumentId: string;
+ linkDatabaseId: string;
+ }) {
+ return DocServer.GetRefField(info.userDocumentId).then(async field => {
+ Docs.newAccount = !(field instanceof Doc);
+ await Docs.Prototypes.initialize();
+ const userDoc = Docs.newAccount ? new Doc(info.userDocumentId, true) : field as Doc;
+ this.updateUserDocument(Doc.SetUserDoc(userDoc), info.sharingDocumentId, info.linkDatabaseId);
+ if (Docs.newAccount) {
+ if (Doc.CurrentUserEmail === "guest") {
+ DashboardView.createNewDashboard(undefined, "guest dashboard");
+ } else {
+ userDoc.activePage = "home";
+ }
}
+ return userDoc;
});
}
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 8e4e0d8f3..42132c2d7 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -75,7 +75,7 @@ export class DocumentManager {
@action
public AddView = (view: DocumentView) => {
- //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
+ if (view.props.LayoutTemplateString?.includes(KeyValueBox.name)) return;
if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const viewAnchorIndex = view.props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1';
const link = view.rootDoc;
@@ -251,6 +251,7 @@ export class DocumentManager {
options: DocFocusOptions, // options for how to navigate to target
finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
) => {
+ Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
let rootContextView =
@@ -335,7 +336,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe
if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) {
DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc));
} else {
- const container = DocCast(containingDoc ?? doc.embedContainer ?? doc);
+ const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc));
const showDoc = !Doc.IsSystem(container) ? container : doc;
options.toggleTarget = undefined;
DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ac948c7c7..489c9df4a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,7 +15,7 @@ import { SelectionManager } from './SelectionManager';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
-export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
+export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
/**
* Initialize drag
@@ -213,6 +213,8 @@ export namespace DragManager {
? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
: docDragData.dropAction === 'embed'
? Doc.BestEmbedding(d)
+ : docDragData.dropAction === 'add'
+ ? d
: docDragData.dropAction === 'proto'
? Doc.GetProto(d)
: docDragData.dropAction === 'copy'
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 5802d5ee0..f35844020 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -15,7 +15,8 @@ import { SharingManager, User } from './SharingManager';
import { listSpec } from '../../fields/Schema';
import { DateField } from '../../fields/DateField';
import { Id } from '../../fields/FieldSymbols';
-import { Button, IconButton, Size } from 'browndash-components';
+import { Button, IconButton, Size, Type } from 'browndash-components';
+import { SettingsManager } from './SettingsManager';
/**
* Interface for options for the react-select component
@@ -281,7 +282,7 @@ export class GroupManager extends React.Component<{}> {
*/
private get groupCreationModal() {
const contents = (
- <div className="group-create" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ <div className="group-create" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
<div className="group-heading" style={{ marginBottom: 0 }}>
<p>
<b>New Group</b>
@@ -297,10 +298,10 @@ export class GroupManager extends React.Component<{}> {
/>
</div>
</div>
- <div className="group-input" style={{border: StrCast(Doc.UserDoc().userColor)}} >
+ <div className="group-input" style={{ border: StrCast(Doc.UserDoc().userColor) }}>
<input ref={this.inputRef} onKeyDown={this.handleKeyDown} autoFocus type="text" placeholder="Group name" onChange={action(() => (this.buttonColour = this.inputRef.current?.value ? 'black' : '#979797'))} />
</div>
- <div style={{border: StrCast(Doc.UserDoc().userColor)}}>
+ <div style={{ border: StrCast(Doc.UserDoc().userColor) }}>
<Select
className="select-users"
isMulti
@@ -331,8 +332,8 @@ export class GroupManager extends React.Component<{}> {
}}
/>
</div>
- <div className={"create-button"}>
- <Button text={"Create"} type={"tertiary"} color={StrCast(Doc.UserDoc().userColor)} onClick={this.createGroup} />
+ <div className={'create-button'}>
+ <Button text={'Create'} type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} onClick={this.createGroup} />
</div>
</div>
);
@@ -366,29 +367,25 @@ export class GroupManager extends React.Component<{}> {
const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups;
return (
- <div className="group-interface" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ <div className="group-interface" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
{this.groupCreationModal}
{this.currentGroup ? <GroupMemberView group={this.currentGroup} onCloseButtonClick={action(() => (this.currentGroup = undefined))} /> : null}
<div className="group-heading">
<p>
<b>Manage Groups</b>
</p>
- <Button icon={<FontAwesomeIcon icon={'plus'}/>} iconPlacement={'left'} text={"Create Group"} type={"tertiary"} color={StrCast(Doc.UserDoc().userColor)} onClick={action(() => (this.createGroupModalOpen = true))} />
- <div className={'close-button'} >
- <Button
- icon={<FontAwesomeIcon icon={'times'} size={'lg'} />}
- onClick={this.close}
- color={StrCast(Doc.UserDoc().userColor)}
- />
+ <Button icon={<FontAwesomeIcon icon={'plus'} />} iconPlacement={'left'} text={'Create Group'} type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} onClick={action(() => (this.createGroupModalOpen = true))} />
+ <div className={'close-button'}>
+ <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={this.close} color={StrCast(Doc.UserDoc().userColor)} />
</div>
</div>
<div className="main-container">
<div className="sort-groups" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
Name
- <IconButton icon={<FontAwesomeIcon icon={this.groupSort === 'ascending' ? 'caret-up' : this.groupSort === 'descending' ? 'caret-down' : 'caret-right'}/>} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} />
+ <IconButton icon={<FontAwesomeIcon icon={this.groupSort === 'ascending' ? 'caret-up' : this.groupSort === 'descending' ? 'caret-down' : 'caret-right'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} />
</div>
- <div className={'style-divider'} style={{background: StrCast(Doc.UserDoc().userColor)}}/>
- <div className="group-body" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ <div className={'style-divider'} style={{ background: StrCast(Doc.UserDoc().userColor) }} />
+ <div className="group-body" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
{groups.map(group => (
<div className="group-row" key={StrCast(group.title || group.groupName)}>
<div className="group-name">{StrCast(group.title || group.groupName)}</div>
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx
index f2050dc61..535d8ccc2 100644
--- a/src/client/util/GroupMemberView.tsx
+++ b/src/client/util/GroupMemberView.tsx
@@ -1,14 +1,15 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import Select from "react-select";
-import { Doc } from "../../fields/Doc";
-import { StrCast } from "../../fields/Types";
-import { MainViewModal } from "../views/MainViewModal";
-import { GroupManager, UserOptions } from "./GroupManager";
-import "./GroupMemberView.scss";
-import { Button, IconButton, Size } from "browndash-components";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import Select from 'react-select';
+import { Doc } from '../../fields/Doc';
+import { StrCast } from '../../fields/Types';
+import { MainViewModal } from '../views/MainViewModal';
+import { GroupManager, UserOptions } from './GroupManager';
+import './GroupMemberView.scss';
+import { Button, IconButton, Size, Type } from 'browndash-components';
+import { SettingsManager } from './SettingsManager';
interface GroupMemberViewProps {
group: Doc;
@@ -17,44 +18,37 @@ interface GroupMemberViewProps {
@observer
export class GroupMemberView extends React.Component<GroupMemberViewProps> {
-
- @observable private memberSort: "ascending" | "descending" | "none" = "none";
+ @observable private memberSort: 'ascending' | 'descending' | 'none' = 'none';
private get editingInterface() {
let members: string[] = this.props.group ? JSON.parse(StrCast(this.props.group.members)) : [];
- members = this.memberSort === "ascending" ? members.sort() : this.memberSort === "descending" ? members.sort().reverse() : members;
+ members = this.memberSort === 'ascending' ? members.sort() : this.memberSort === 'descending' ? members.sort().reverse() : members;
const options: UserOptions[] = this.props.group ? GroupManager.Instance.options.filter(option => !(JSON.parse(StrCast(this.props.group.members)) as string[]).includes(option.value)) : [];
const hasEditAccess = GroupManager.Instance.hasEditAccess(this.props.group);
- return (!this.props.group ? null :
- <div className="editing-interface" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ return !this.props.group ? null : (
+ <div className="editing-interface" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
<div className="editing-header">
<input
className="group-title"
- style={{ marginLeft: !hasEditAccess ? "-14%" : 0 }}
+ style={{ marginLeft: !hasEditAccess ? '-14%' : 0 }}
value={StrCast(this.props.group.title || this.props.group.groupName)}
- onChange={e => this.props.group.title = e.currentTarget.value}
- disabled={!hasEditAccess}
- >
- </input>
- <div className={"memberView-closeButton"} >
- <Button
- icon={<FontAwesomeIcon icon={'times'} size={'lg'} />}
- onClick={action(this.props.onCloseButtonClick)}
- color={StrCast(Doc.UserDoc().userColor)}
- />
+ onChange={e => (this.props.group.title = e.currentTarget.value)}
+ disabled={!hasEditAccess}></input>
+ <div className={'memberView-closeButton'}>
+ <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={action(this.props.onCloseButtonClick)} color={StrCast(Doc.UserDoc().userColor)} />
</div>
- {GroupManager.Instance.hasEditAccess(this.props.group) ?
+ {GroupManager.Instance.hasEditAccess(this.props.group) ? (
<div className="group-buttons">
- <div style={{border: StrCast(Doc.UserDoc().userColor)}} >
- <Select
+ <div style={{ border: StrCast(Doc.UserDoc().userColor) }}>
+ <Select
className="add-member-dropdown"
isSearchable={true}
options={options}
onChange={selectedOption => GroupManager.Instance.addMemberToGroup(this.props.group, (selectedOption as UserOptions).value)}
- placeholder={"Add members"}
+ placeholder={'Add members'}
value={null}
styles={{
control: () => ({
@@ -78,52 +72,33 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> {
}}
/>
</div>
- <div className={"delete-button"}>
- <Button text={"Delete Group"} type={"tertiary"} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.deleteGroup(this.props.group)} />
+ <div className={'delete-button'}>
+ <Button text={'Delete Group'} type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.deleteGroup(this.props.group)} />
</div>
- </div> :
- null}
- <div
- className="sort-emails"
- style={{ paddingTop: hasEditAccess ? 0 : 35 }}
- onClick={action(() => this.memberSort = this.memberSort === "ascending" ? "descending" : this.memberSort === "descending" ? "none" : "ascending")}>
- Emails {this.memberSort === "ascending" ? "↑" : this.memberSort === "descending" ? "↓" : ""} {/* → */}
+ </div>
+ ) : null}
+ <div className="sort-emails" style={{ paddingTop: hasEditAccess ? 0 : 35 }} onClick={action(() => (this.memberSort = this.memberSort === 'ascending' ? 'descending' : this.memberSort === 'descending' ? 'none' : 'ascending'))}>
+ Emails {this.memberSort === 'ascending' ? '↑' : this.memberSort === 'descending' ? '↓' : ''} {/* → */}
</div>
</div>
- <div className={'style-divider'} style={{background: StrCast(Doc.UserDoc().userColor)}}/>
- <div className="editing-contents"
- style={{ height: hasEditAccess ? "62%" : "85%" }}
- >
+ <div className={'style-divider'} style={{ background: StrCast(Doc.UserDoc().userColor) }} />
+ <div className="editing-contents" style={{ height: hasEditAccess ? '62%' : '85%' }}>
{members.map(member => (
- <div
- className="editing-row"
- key={member}
- >
- <div className="user-email">
- {member}
- </div>
- {hasEditAccess ?
- <div className={"remove-button"} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.props.group, member)}>
+ <div className="editing-row" key={member}>
+ <div className="user-email">{member}</div>
+ {hasEditAccess ? (
+ <div className={'remove-button'} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.props.group, member)}>
<IconButton icon={<FontAwesomeIcon icon={'trash-alt'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.props.group, member)} />
</div>
- : null}
+ ) : null}
</div>
))}
</div>
</div>
);
-
}
render() {
- return <MainViewModal
- isDisplayed={true}
- interactive={true}
- contents={this.editingInterface}
- dialogueBoxStyle={{ width: 400, height: 250 }}
- closeOnExternalClick={this.props.onCloseButtonClick}
- />;
+ return <MainViewModal isDisplayed={true} interactive={true} contents={this.editingInterface} dialogueBoxStyle={{ width: 400, height: 250 }} closeOnExternalClick={this.props.onCloseButtonClick} />;
}
-
-
-} \ No newline at end of file
+}
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 3e526c4c0..b8fea340f 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -30,7 +30,7 @@ export class LinkFollower {
public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, altKey: boolean) => {
const batch = UndoManager.StartBatch('Follow Link');
runInAction(() => (LinkFollower.IsFollowing = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value
- LinkFollower.traverseLink(
+ return LinkFollower.traverseLink(
linkDoc,
sourceDoc,
action(() => {
@@ -54,7 +54,10 @@ export class LinkFollower {
const followLinks = sourceDoc.followLinkToggle || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1);
var count = 0;
const allFinished = () => ++count === followLinks.length && finished?.();
- if (!followLinks.length) finished?.();
+ if (!followLinks.length) {
+ finished?.();
+ return false;
+ }
followLinks.forEach(async linkDoc => {
const target = (
sourceDoc === linkDoc.link_anchor_1
@@ -120,17 +123,18 @@ export class LinkFollower {
allFinished();
}
});
+ return true;
}
}
ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) {
SelectionManager.DeselectAll();
- LinkFollower.FollowLink(undefined, doc, altKey);
+ return LinkFollower.FollowLink(undefined, doc, altKey) ? undefined : { select: true };
});
export function FollowLinkScript() {
- return ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' });
+ return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' });
}
export function IsFollowLinkScript(field: FieldResult<Field>) {
- return ScriptCast(field)?.script.originalScript.includes('followLink(');
+ return ScriptCast(field)?.script.originalScript.includes('return followLink(');
}
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index c7f092565..ef4b21b05 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,8 @@
-import { action, observable, observe } from 'mobx';
+import { action, observable, observe, runInAction } from 'mobx';
import { computedFn } from 'mobx-utils';
import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
import { DirectLinks } from '../../fields/DocSymbols';
+import { FieldLoader } from '../../fields/FieldLoader';
import { List } from '../../fields/List';
import { ProxyField } from '../../fields/Proxy';
import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
@@ -132,6 +133,7 @@ export class LinkManager {
},
true
);
+ runInAction(() => (FieldLoader.ServerLoadStatus.message = 'links'));
LinkManager.addLinkDB(Doc.LinkDBDoc());
}
diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts
index 0c41a1ea7..4dd2fcd35 100644
--- a/src/client/util/PingManager.ts
+++ b/src/client/util/PingManager.ts
@@ -1,5 +1,6 @@
-import { action, observable } from 'mobx';
+import { action, observable, runInAction } from 'mobx';
import { Networking } from '../Network';
+import { CurrentUserUtils } from './CurrentUserUtils';
export class PingManager {
// create static instance and getter for global use
@observable static _instance: PingManager;
@@ -18,7 +19,8 @@ export class PingManager {
};
sendPing = async (): Promise<void> => {
try {
- await Networking.PostToServer('/ping', { date: new Date() });
+ const res = await Networking.PostToServer('/ping', { date: new Date() });
+ runInAction(() => (CurrentUserUtils.ServerVersion = res.message));
!this.IsBeating && this.setIsBeating(true);
} catch {
if (this.IsBeating) {
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index b93d4f293..c2f121e1f 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -30,7 +30,7 @@ export class RTFMarkup extends React.Component<{}> {
*/
@computed get cheatSheet() {
return (
- <div style={{ textAlign: 'initial', height: '100%' }}>
+ <div style={{ background: 'white', textAlign: 'initial', height: '100%' }}>
<p>
<b style={{ fontSize: 'larger' }}>{`wiki:phrase`}</b>
{` display wikipedia page for entered text (terminate with carriage return)`}
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index cbc465d6a..d99630f82 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -187,7 +187,6 @@ export class ReplayMovements {
} else {
// tab wasn't open - open it and play the movement
const openedColFFView = this.openTab(movement.doc);
- console.log('openedColFFView', openedColFFView);
openedColFFView && this.zoomAndPan(movement, openedColFFView);
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 0125331ec..4be9448b3 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -121,3 +121,6 @@ ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, exp
let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
return selected?.type === type || selected?.type_collection === type || !type;
});
+ScriptingGlobals.add(function deselectAll() {
+ SelectionManager.DeselectAll();
+});
diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
index 6a6ec158e..3c7c35a7e 100644
--- a/src/client/util/ServerStats.tsx
+++ b/src/client/util/ServerStats.tsx
@@ -6,6 +6,7 @@ import './SharingManager.scss';
import { PingManager } from './PingManager';
import { StrCast } from '../../fields/Types';
import { Doc } from '../../fields/Doc';
+import { SettingsManager } from './SettingsManager';
@observer
export class ServerStats extends React.Component<{}> {
@@ -42,21 +43,22 @@ export class ServerStats extends React.Component<{}> {
*/
@computed get sharingInterface() {
return (
- <div style={{
- display: "flex",
- height: 300,
- width: 400,
- background: StrCast(Doc.UserDoc().userBackgroundColor),
- opacity: 0.6}}>
- <div style={{width: 300,height: 100,margin: "auto",display: "flex",flexDirection: "column"}}>
- {PingManager.Instance.IsBeating ? 'The server connection is active' :
- 'The server connection has been interrupted.NOTE: Any changes made will appear to persist but will be lost after a browser refreshes.'}
-
- <br/>
+ <div
+ style={{
+ display: 'flex',
+ height: 300,
+ width: 400,
+ background: SettingsManager.Instance?.userBackgroundColor,
+ opacity: 0.6,
+ }}>
+ <div style={{ width: 300, height: 100, margin: 'auto', display: 'flex', flexDirection: 'column' }}>
+ {PingManager.Instance.IsBeating ? 'The server connection is active' : 'The server connection has been interrupted.NOTE: Any changes made will appear to persist but will be lost after a browser refreshes.'}
+
+ <br />
<span>Active users:{this._stats?.socketMap.length}</span>
{this._stats?.socketMap.map((user: any) => (
<p>{user.username}</p>
- ))}
+ ))}
</div>
</div>
);
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index b8e327968..b2b5be070 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,10 +1,12 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { ColorState, SketchPicker } from 'react-color';
+import { BsGoogle } from 'react-icons/bs';
+import { FaFillDrip, FaPalette } from 'react-icons/fa';
import { Doc } from '../../fields/Doc';
-import { Id } from '../../fields/FieldSymbols';
+import { DashVersion } from '../../fields/DocSymbols';
import { BoolCast, Cast, StrCast } from '../../fields/Types';
import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
@@ -12,13 +14,9 @@ import { DocServer } from '../DocServer';
import { Networking } from '../Network';
import { MainViewModal } from '../views/MainViewModal';
import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
-import { DragManager } from './DragManager';
import { GroupManager } from './GroupManager';
import './SettingsManager.scss';
import { undoBatch } from './UndoManager';
-import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { BsGoogle } from 'react-icons/bs';
-import { FaFillDrip, FaPalette } from 'react-icons/fa';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -452,6 +450,7 @@ export class SettingsManager extends React.Component<{}> {
</div>
<div className="settings-user">
+ <div style={{ color: 'black' }}>{DashVersion}</div>
<div className="settings-username" style={{ color: this.userBackgroundColor }}>
{Doc.CurrentUserEmail}
</div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 795df9c08..6171c01d7 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, IconButton, Size } from 'browndash-components';
+import { Button, IconButton, Size, Type } from 'browndash-components';
import { concat, intersection } from 'lodash';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -8,6 +8,7 @@ import Select from 'react-select';
import * as RequestPromise from 'request-promise';
import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
import { AclAdmin, AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols';
+import { FieldLoader } from '../../fields/FieldLoader';
import { Id } from '../../fields/FieldSymbols';
import { StrCast } from '../../fields/Types';
import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
@@ -23,6 +24,7 @@ import { GroupManager, UserOptions } from './GroupManager';
import { GroupMemberView } from './GroupMemberView';
import { LinkManager } from './LinkManager';
import { SelectionManager } from './SelectionManager';
+import { SettingsManager } from './SettingsManager';
import './SharingManager.scss';
import { undoable } from './UndoManager';
@@ -136,6 +138,7 @@ export class SharingManager extends React.Component<{}> {
this.populating = true;
const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
const raw = (JSON.parse(userList) as User[]).filter(user => user.email !== 'guest' && user.email !== Doc.CurrentUserEmail);
+ runInAction(() => (FieldLoader.ServerLoadStatus.message = 'users'));
const docs = await DocServer.GetRefFields(raw.reduce((list, user) => [...list, user.sharingDocumentId, user.linkDatabaseId], [] as string[]));
raw.map(
action((newUser: User) => {
@@ -144,7 +147,7 @@ export class SharingManager extends React.Component<{}> {
if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
if (!this.users.find(user => user.user.email === newUser.email)) {
this.users.push({ user: newUser, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) });
- LinkManager.addLinkDB(linkDatabase);
+ //LinkManager.addLinkDB(linkDatabase);
}
}
})
@@ -525,12 +528,10 @@ export class SharingManager extends React.Component<{}> {
const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
return !permissions ? null : (
- <div key={groupKey} className={'container'} style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ <div key={groupKey} className={'container'} style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
<div className={'padding'}>{StrCast(group.title)}</div>
&nbsp;
- {group instanceof Doc ? (
- <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={action(() => (GroupManager.Instance.currentGroup = group))} />
- ) : null}
+ {group instanceof Doc ? <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={SettingsManager.Instance.userColor} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null}
<div className={'edit-actions'}>
{admin || this.myDocAcls ? (
<select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}>
@@ -549,32 +550,28 @@ export class SharingManager extends React.Component<{}> {
return (
<div className="sharing-interface">
{GroupManager.Instance?.currentGroup ? <GroupMemberView group={GroupManager.Instance.currentGroup} onCloseButtonClick={action(() => (GroupManager.Instance.currentGroup = undefined))} /> : null}
- <div className="sharing-contents"
+ <div
+ className="sharing-contents"
style={{
- background: StrCast(Doc.UserDoc().userBackgroundColor),
- color: StrCast(Doc.UserDoc().userColor)
- }}
- >
- <p className="share-title" style={{color: StrCast(Doc.UserDoc().userColor)}}>
+ background: SettingsManager.Instance.userBackgroundColor,
+ color: StrCast(Doc.UserDoc().userColor),
+ }}>
+ <p className="share-title" style={{ color: StrCast(Doc.UserDoc().userColor) }}>
<div className="share-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/collaboration/', '_blank')}>
<FontAwesomeIcon icon={'question-circle'} size={'sm'} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/collaboration/', '_blank')} />
</div>
- <b >Share </b>
+ <b>Share </b>
{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}
</p>
<div className="share-copy-link">
- <Button type={"tertiary"} color={StrCast(Doc.UserDoc().userColor)} icon={<FontAwesomeIcon icon={"copy"} size="sm" />} iconPlacement={"left"} text={"Copy Guest URL"} />
+ <Button type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} icon={<FontAwesomeIcon icon={'copy'} size="sm" />} iconPlacement={'left'} text={'Copy Guest URL'} onClick={this.copyURL} />
</div>
<div className="close-button">
- <Button
- icon={<FontAwesomeIcon icon={'times'} size={'lg'} />}
- onClick={this.close}
- color={StrCast(Doc.UserDoc().userColor)}
- />
+ <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={this.close} color={StrCast(Doc.UserDoc().userColor)} />
</div>
{admin ? (
<div className="share-container">
- <div className="share-setup" style={{border: StrCast(Doc.UserDoc().userColor)}}>
+ <div className="share-setup" style={{ border: StrCast(Doc.UserDoc().userColor) }}>
<Select
className="user-search"
placeholder="Enter user or group name..."
@@ -611,8 +608,8 @@ export class SharingManager extends React.Component<{}> {
{this.sharingOptions(true)}
</select>
</div>
- <div className='share-button'>
- <Button text={"SHARE"} type={"tertiary"} color={StrCast(Doc.UserDoc().userColor)} onClick={this.share} />
+ <div className="share-button">
+ <Button text={'SHARE'} type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} onClick={this.share} />
</div>
</div>
<div className="sort-checkboxes">
@@ -638,22 +635,30 @@ export class SharingManager extends React.Component<{}> {
</div>
</div>
)}
- <div className="main-container" style={{color: StrCast(Doc.UserDoc().userColor), border: StrCast(Doc.UserDoc().userColor)}}>
+ <div className="main-container" style={{ color: StrCast(Doc.UserDoc().userColor), border: StrCast(Doc.UserDoc().userColor) }}>
<div className={'individual-container'}>
<div className="user-sort" onClick={action(() => (this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}>
<div className="title-individual">
Individuals
- <IconButton icon={<FontAwesomeIcon icon={this.individualSort === 'ascending' ? 'caret-up' : this.individualSort === 'descending' ? 'caret-down' : 'caret-right'}/>} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} />
+ <IconButton
+ icon={<FontAwesomeIcon icon={this.individualSort === 'ascending' ? 'caret-up' : this.individualSort === 'descending' ? 'caret-down' : 'caret-right'} />}
+ size={Size.XSMALL}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
</div>
</div>
<div className="users-list">{userListContents}</div>
</div>
<div className={'group-container'}>
<div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
- <div className="title-group" >
+ <div className="title-group">
Groups
- <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={action(() => (GroupManager.Instance.open()))} />
- <IconButton icon={<FontAwesomeIcon icon={this.groupSort === 'ascending' ? 'caret-up' : this.groupSort === 'descending' ? 'caret-down' : 'caret-right'}/>} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} />
+ <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={action(() => GroupManager.Instance.open())} />
+ <IconButton
+ icon={<FontAwesomeIcon icon={this.groupSort === 'ascending' ? 'caret-up' : this.groupSort === 'descending' ? 'caret-down' : 'caret-right'} />}
+ size={Size.XSMALL}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
</div>
</div>
<div className={'groups-list'}>{groupListContents}</div>
@@ -665,13 +670,6 @@ export class SharingManager extends React.Component<{}> {
}
render() {
- return <MainViewModal
- contents={this.sharingInterface}
- isDisplayed={this.isOpen}
- interactive={true}
- dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
- overlayDisplayedOpacity={this.overlayOpacity}
- closeOnExternalClick={this.close}
- />;
+ return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
}
}
diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx
index e684bd637..7aad0f2b1 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -19,6 +19,7 @@ import { BugType, FileData, Priority, ReportForm, ViewState, bugDropdownItems, d
import { Filter, FormInput, FormTextArea, IssueCard, IssueView, Tag } from './ReportManagerComponents';
import { StrCast } from '../../../fields/Types';
import { MdRefresh } from 'react-icons/md';
+import { SettingsManager } from '../SettingsManager';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -213,11 +214,11 @@ export class ReportManager extends React.Component<{}> {
* @returns the component that dispays all issues
*/
private viewIssuesComponent = () => {
- const darkMode = isDarkMode(StrCast(Doc.UserDoc().userBackgroundColor));
+ const darkMode = isDarkMode(SettingsManager.Instance.userBackgroundColor);
const colors = darkMode ? darkColors : lightColors;
return (
- <div className="view-issues" style={{ backgroundColor: StrCast(Doc.UserDoc().userBackgroundColor), color: colors.text }}>
+ <div className="view-issues" style={{ backgroundColor: SettingsManager.Instance.userBackgroundColor, color: colors.text }}>
<div className="left" style={{ display: this.rightExpanded ? 'none' : 'flex' }}>
<div className="report-header">
<h2 style={{ color: colors.text }}>Open Issues</h2>
diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx
index 14c922526..c779ce8c4 100644
--- a/src/client/views/AudioWaveform.tsx
+++ b/src/client/views/AudioWaveform.tsx
@@ -3,7 +3,7 @@ import axios from 'axios';
import { action, computed, IReactionDisposer, reaction } from 'mobx';
import { observer } from 'mobx-react';
import Waveform from 'react-audio-waveform';
-import { Doc } from '../../fields/Doc';
+import { Doc, NumListCast } from '../../fields/Doc';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
import { Cast } from '../../fields/Types';
@@ -54,7 +54,7 @@ export class AudioWaveform extends React.Component<AudioWaveformProps> {
}
@computed get audioBuckets() {
- return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)], listSpec('number'), []);
+ return NumListCast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)]);
}
audioBucketField = (start: number, end: number, zoomFactor: number) => this.props.fieldKey + '_audioBuckets/' + '/' + start.toFixed(2).replace('.', '_') + '/' + end.toFixed(2).replace('.', '_') + '/' + zoomFactor * 10;
@@ -102,7 +102,7 @@ export class AudioWaveform extends React.Component<AudioWaveformProps> {
render() {
return (
<div className="audioWaveform">
- <Waveform color={Colors.MEDIUM_BLUE_ALT} height={this.waveHeight} barWidth={200 / this.audioBuckets.length} pos={this.props.duration} duration={this.props.duration} peaks={this.audioBuckets} progressColor={Colors.MEDIUM_BLUE_ALT} />
+ <Waveform color={Colors.MEDIUM_BLUE_ALT} height={this.waveHeight} barWidth={200 / this.audioBuckets.length} pos={this.props.duration} duration={this.props.duration} peaks={Array.from(this.audioBuckets)} progressColor={Colors.MEDIUM_BLUE_ALT} />
</div>
);
}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index ae55c8ebf..21808d6e0 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -43,7 +43,7 @@ export class DashboardView extends React.Component {
@observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
@observable private newDashboardName: string | undefined = undefined;
- @observable private newDashboardColor: string | undefined = "#AFAFAF";
+ @observable private newDashboardColor: string | undefined = '#AFAFAF';
@action abortCreateNewDashboard = () => {
this.newDashboardName = undefined;
};
@@ -100,30 +100,15 @@ export class DashboardView extends React.Component {
const dashboardCount = DocListCast(Doc.MyDashboards.data).length + 1;
const placeholder = `Dashboard ${dashboardCount}`;
return (
- <div className="new-dashboard"
- style={{
+ <div
+ className="new-dashboard"
+ style={{
background: StrCast(Doc.UserDoc().userBackgroundColor),
- color: StrCast(Doc.UserDoc().userColor)
- }}
- >
+ color: StrCast(Doc.UserDoc().userColor),
+ }}>
<div className="header">Create New Dashboard</div>
- <EditableText
- formLabel='Title'
- placeholder={placeholder}
- type={Type.SEC}
- color={StrCast(Doc.UserDoc().userColor)}
- setVal={val => this.setNewDashboardName(val as string)}
- fillWidth
- />
- <ColorPicker
- formLabel='Background'
- colorPickerType='github'
- type={Type.TERT}
- selectedColor={this.newDashboardColor}
- setSelectedColor={color => {
- this.setNewDashboardColor(color);
- }}
- />
+ <EditableText formLabel="Title" placeholder={placeholder} type={Type.SEC} color={StrCast(Doc.UserDoc().userColor)} setVal={val => this.setNewDashboardName(val as string)} fillWidth />
+ <ColorPicker formLabel="Background" colorPickerType="github" type={Type.TERT} selectedColor={this.newDashboardColor} setSelectedColor={this.setNewDashboardColor} />
<div className="button-bar">
<Button text="Cancel" color={StrCast(Doc.UserDoc().userColor)} onClick={this.abortCreateNewDashboard} />
<Button type={Type.TERT} text="Create" color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.createNewDashboard(this.newDashboardName!, this.newDashboardColor)} />
@@ -165,21 +150,14 @@ export class DashboardView extends React.Component {
};
render() {
- const color = StrCast(Doc.UserDoc().userColor)
- const variant = StrCast(Doc.UserDoc().userVariantColor)
+ const color = StrCast(Doc.UserDoc().userColor);
+ const variant = StrCast(Doc.UserDoc().userVariantColor);
return (
<>
<div className="dashboard-view">
<div className="left-menu">
- <Button
- text={'My Dashboards'}
- active={this.selectedDashboardGroup === DashboardGroup.MyDashboards}
- color={color}
- align={'flex-start'}
- onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}
- fillWidth
- />
- <Button
+ <Button text={'My Dashboards'} active={this.selectedDashboardGroup === DashboardGroup.MyDashboards} color={color} align={'flex-start'} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)} fillWidth />
+ <Button
text={'Shared Dashboards' + ' (' + this.getDashboards(DashboardGroup.SharedDashboards).length + ')'}
active={this.selectedDashboardGroup === DashboardGroup.SharedDashboards}
color={this.getDashboards(DashboardGroup.SharedDashboards).some(dash => !DocListCast(Doc.MySharedDocs.viewed).includes(dash)) ? 'green' : color}
@@ -196,11 +174,11 @@ export class DashboardView extends React.Component {
.filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Guest'].includes(key))
.some(key => dashboard[DocAcl][key] !== AclPrivate);
return (
- <div
- className="dashboard-container"
- key={dashboard[Id]}
+ <div
+ className="dashboard-container"
+ key={dashboard[Id]}
style={{ background: this.isUnviewedSharedDashboard(dashboard) && this.selectedDashboardGroup === DashboardGroup.SharedDashboards ? '#6CB982' : shared ? variant : '' }}
- onContextMenu={e => this.onContextMenu(dashboard, e)}
+ onContextMenu={e => this.onContextMenu(dashboard, e)}
onClick={e => this.clickDashboard(e, dashboard)}>
<img
src={
@@ -208,12 +186,7 @@ export class DashboardView extends React.Component {
}
/>
<div className="info">
- <EditableText
- type={Type.PRIM}
- color={color}
- val={StrCast(dashboard.title)}
- setVal={val => (Doc.GetProto(dashboard).title = val)}
- />
+ <EditableText type={Type.PRIM} color={color} val={StrCast(dashboard.title)} setVal={val => (Doc.GetProto(dashboard).title = val)} />
{this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></div>}
<div
className="more"
@@ -229,10 +202,13 @@ export class DashboardView extends React.Component {
<Button size={Size.SMALL} color={color} icon={<FontAwesomeIcon color={color} icon="bars" />} />
</div>
</div>
- <div className={`background`} style={{
- background: StrCast(Doc.UserDoc().userColor),
- filter: 'opacity(0.2)'
- }}/>
+ <div
+ className={`background`}
+ style={{
+ background: StrCast(Doc.UserDoc().userColor),
+ filter: 'opacity(0.2)',
+ }}
+ />
<div className={'dashboard-status' + (shared ? '-shared' : '')}>{shared ? 'shared' : ''}</div>
</div>
);
@@ -243,10 +219,13 @@ export class DashboardView extends React.Component {
this.setNewDashboardName('');
}}>
+
- <div className={`background`} style={{
- background: StrCast(Doc.UserDoc().userColor),
- filter: 'opacity(0.2)'
- }}/>
+ <div
+ className={`background`}
+ style={{
+ background: StrCast(Doc.UserDoc().userColor),
+ filter: 'opacity(0.2)',
+ }}
+ />
</div>
</div>
</div>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index a41fc8ded..e076e69ca 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -3,7 +3,7 @@ import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, HierarchyMapping, Opt, ReverseHierarchyMap } from '../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
-import { Cast, StrCast } from '../../fields/Types';
+import { Cast, DocCast, StrCast } from '../../fields/Types';
import { distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
import { returnFalse } from '../../Utils';
import { DocUtils } from '../documents/Documents';
@@ -145,13 +145,13 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
const toRemove = value.filter(v => docs.includes(v));
if (toRemove.length !== 0) {
- const recent = Doc.MyRecentlyClosed;
+ const recent = this.rootDoc !== Doc.MyRecentlyClosed ? Doc.MyRecentlyClosed : undefined;
toRemove.forEach(doc => {
leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
+ Doc.RemoveDocFromList(Doc.GetProto(doc), 'proto_embeddings', doc);
doc.embedContainer = undefined;
if (recent) {
- Doc.RemoveDocFromList(recent, 'data', doc);
doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
}
});
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 16f5ad168..6f5e9f5c0 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -589,7 +589,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
<div style={{ position: 'absolute', zIndex: 1000 }}>
<LinkPopup
key="popup"
- showPopup={this._showLinkPopup}
linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
linkFrom={() => this.props.views().lastElement()?.rootDoc}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index bab07ac96..f3daf3ffa 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -617,8 +617,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
let actualdW = Math.max(docwidth + dW * scale, 20);
let actualdH = Math.max(docheight + dH * scale, 20);
- let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth));
- let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight));
+ let dX = !dWin ? 0 : (scale * refCent[0] * -dWin) / refWidth;
+ let dY = !dHin ? 0 : (scale * refCent[1] * -dHin) / refHeight;
const preserveNativeDim = !doc._nativeHeightUnfrozen && !doc._nativeDimModifiable;
const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
if (fixedAspect) {
@@ -630,6 +630,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
} else {
if (!doc._layout_fitWidth || preserveNativeDim) {
actualdH = (nheight / nwidth) * actualdW;
+ dYin && (dY = -dW * scale * (nheight / nwidth));
doc._height = actualdH;
} else if (!modifyNativeDim || dragBotRight) {
doc._height = actualdH;
@@ -646,6 +647,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
} else {
if (!doc._layout_fitWidth || preserveNativeDim) {
actualdW = (nwidth / nheight) * actualdH;
+ dXin && (dX = -dH * scale * (nwidth / nheight));
doc._width = actualdW;
} else if (!modifyNativeDim || dragBotRight) {
doc._width = actualdW;
@@ -911,7 +913,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
width: bounds.r - bounds.x + this._resizeBorderWidth + 'px',
height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px',
}}>
- <div className="documentDecorations-topbar" style={{ display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined }} onPointerDown={this.onContainerDown}>
+ <div
+ className="documentDecorations-topbar"
+ style={{
+ color: 'black',
+ display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined,
+ }}
+ onPointerDown={this.onContainerDown}>
{hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
{hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
{titleArea}
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 4a986cb54..d60617020 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -232,7 +232,7 @@ export class EditableView extends React.Component<EditableProps> {
onChange: this.props.autosuggestProps.onChange,
}}
/>
- ) :
+ ) : (
<input
className="editableView-input"
ref={r => (this._inputref = r)}
@@ -248,6 +248,7 @@ export class EditableView extends React.Component<EditableProps> {
onClick={this.stopPropagation}
onPointerUp={this.stopPropagation}
/>
+ );
// ) : (
// <textarea
// className="editableView-input"
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 68d29942b..63bd01b19 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -169,8 +169,6 @@ export class FilterPanel extends React.Component<filterProps> {
render() {
const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
- console.log("this is option " + options)
- console.log("this is alll facets " + this._allFacets)
return (
<div className="filterBox-treeView">
<div className="filterBox-select">
@@ -192,17 +190,15 @@ export class FilterPanel extends React.Component<filterProps> {
<div className="filterBox-tree" key="tree">
{Array.from(this.activeFacets.keys()).map(facetHeader => (
<div>
- <div className = "filterBox-facetHeader">
- <div className = "filterBox-facetHeader-Header"> </div>
+ <div className="filterBox-facetHeader">
+ <div className="filterBox-facetHeader-Header"> </div>
{facetHeader.charAt(0).toUpperCase() + facetHeader.slice(1)}
-
- <div className = "filterBox-facetHeader-collapse">
- <AiOutlineMinusSquare/>
- {/* <CiCircleRemove/> */}
- </div>
-
+
+ <div className="filterBox-facetHeader-collapse">
+ <AiOutlineMinusSquare />
+ {/* <CiCircleRemove/> */}
+ </div>
</div>
-
{this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader)}
</div>
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index c39fa79da..6c213f40f 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -105,7 +105,7 @@ export class InkTranscription extends React.Component {
: null;
}
- r.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
+ r?.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
return (this._textRef = r);
};
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 286d39943..f79a30ad3 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -313,8 +313,10 @@ export class LightboxView extends React.Component<LightboxViewProps> {
className="lightboxView-tabBtn"
title="open in tab"
onClick={e => {
+ const lightdoc = LightboxView._docTarget || LightboxView._doc!;
e.stopPropagation();
- CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none);
+ Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
+ CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
SelectionManager.DeselectAll();
LightboxView.SetLightboxDoc(undefined);
}}>
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 2fa42d091..6dd1d53ee 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -6,10 +6,7 @@ import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { AssignAllExtensions } from '../../extensions/General/Extensions';
import { FieldLoader } from '../../fields/FieldLoader';
-import { DocServer } from '../DocServer';
-import { Docs } from '../documents/Documents';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { LinkManager } from '../util/LinkManager'; // this must come before importing Docs and CurrentUserUtils
import { ReplayMovements } from '../util/ReplayMovements';
import { TrackMovements } from '../util/TrackMovements';
import { CollectionView } from './collections/CollectionView';
@@ -20,7 +17,7 @@ import './global/globalScripts';
dotenv.config();
AssignAllExtensions();
-FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure why this is needed to get the code loaded properly...
+FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; // bcz: not sure why this is needed to get the code loaded properly...
(async () => {
MainView.Live = window.location.search.includes('live');
@@ -29,7 +26,11 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure
window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
const info = await CurrentUserUtils.loadCurrentUser();
// if (info.email === 'guest') DocServer.Control.makeReadOnly();
- await CurrentUserUtils.loadUserDocument(info.id);
+ if (!info.userDocumentId) {
+ alert('Fatal Error: user not found in database');
+ return;
+ }
+ await CurrentUserUtils.loadUserDocument(info);
setTimeout(() => {
document.getElementById('root')!.addEventListener(
'wheel',
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 41c8dd19d..60eb64caa 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -4,7 +4,7 @@ import * as far from '@fortawesome/free-regular-svg-icons';
import * as fa from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import 'browndash-components/dist/styles/global.min.css';
-import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
+import { action, computed, configure, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import 'normalize.css';
import * as React from 'react';
@@ -49,7 +49,6 @@ import { InkTranscription } from './InkTranscription';
import { LightboxView } from './LightboxView';
import { LinkMenu } from './linking/LinkMenu';
import './MainView.scss';
-import { NewLightboxView } from './newlightbox/NewLightboxView';
import { AudioBox } from './nodes/AudioBox';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
@@ -194,14 +193,6 @@ export class MainView extends React.Component {
'currentFrame',
]); // can play with these fields on someone else's
}
- DocServer.GetRefField('rtfProto').then(
- proto =>
- proto instanceof Doc &&
- reaction(
- () => StrCast(proto.BROADCAST_MESSAGE),
- msg => msg && alert(msg)
- )
- );
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
@@ -226,7 +217,7 @@ export class MainView extends React.Component {
window.removeEventListener('keydown', KeyManager.Instance.handle);
window.removeEventListener('pointerdown', this.globalPointerDown, true);
window.removeEventListener('pointermove', this.globalPointerMove, true);
- window.removeEventListener('mouseclick', this.globalPointerClick, true);
+ window.removeEventListener('pointerup', this.globalPointerClick, true);
window.removeEventListener('paste', KeyManager.Instance.paste as any);
document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener);
}
@@ -539,13 +530,13 @@ export class MainView extends React.Component {
});
initEventListeners = () => {
- window.addEventListener('beforeunload', () => DocServer.UPDATE_SERVER_CACHE());
+ window.addEventListener('beforeunload', DocServer.UPDATE_SERVER_CACHE);
window.addEventListener('drop', e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page
window.addEventListener('dragover', e => e.preventDefault(), false);
// document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
document.addEventListener('pointerdown', this.globalPointerDown, true);
document.addEventListener('pointermove', this.globalPointerMove, true);
- document.addEventListener('mouseclick', this.globalPointerClick, true);
+ document.addEventListener('pointerup', this.globalPointerClick, true);
document.addEventListener(
'click',
(e: MouseEvent) => {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 2e3668268..8cae34d7d 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -244,10 +244,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@computed get layout_autoHeightButton() {
// store previous dimensions to store old values
return this.propertyToggleBtn(
- on => 'Auto\xA0Size',
+ on => (on ? 'AUTO\xA0SIZE' : 'FIXED SIZE'),
'_layout_autoHeight',
on => `Automatical vertical sizing to show all content`,
- on => 'arrows-alt-v'
+ on => <FontAwesomeIcon icon="arrows-alt-v" size="lg" />
);
}
@@ -375,7 +375,6 @@ export class PropertiesButtons extends React.Component<{}, {}> {
val: value[1],
};
});
- console.log('click val: ', this.onClickVal);
return !this.selectedDoc ? null : (
<Dropdown
tooltip={'Choose onClick behavior'}
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 395aa2b61..d157e7b1c 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -38,7 +38,6 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
}, new Set<Doc>())
.keys()
);
- console.log('embeddings ' + embeddings.length);
return doclayouts
.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document))
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
index ec8043ffe..6fab0168b 100644
--- a/src/client/views/PropertiesSection.tsx
+++ b/src/client/views/PropertiesSection.tsx
@@ -1,19 +1,19 @@
import React = require('react');
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
-import { action, computed, observable } from "mobx"
-import { observer } from "mobx-react"
-import './PropertiesSection.scss'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import './PropertiesSection.scss';
import { Doc } from '../../fields/Doc';
import { StrCast } from '../../fields/Types';
export interface PropertiesSectionProps {
- title: string,
- content?: JSX.Element | string | null,
- isOpen: boolean,
- setIsOpen: (bool: boolean) => any
- inSection?: boolean,
- setInSection?: (bool: boolean) => any
- onDoubleClick?: () => void
+ title: string;
+ content?: JSX.Element | string | null;
+ isOpen: boolean;
+ setIsOpen: (bool: boolean) => any;
+ inSection?: boolean;
+ setInSection?: (bool: boolean) => any;
+ onDoubleClick?: () => void;
}
@observer
@@ -33,34 +33,32 @@ export class PropertiesSection extends React.Component<PropertiesSectionProps> {
@observable isDouble: boolean = false;
render() {
- console.log(this.props.title, this.props.content)
- if (this.props.content === undefined || this.props.content === null) return null
- else return <div className="propertiesView-section" onPointerEnter={action(() => (this.props.setInSection && this.props.setInSection(true)))} onPointerLeave={action(() => (this.props.setInSection && this.props.setInSection(false)))}>
- <div className="propertiesView-sectionTitle"
- onDoubleClick={action((e) => {
- this.isDouble = true;
- this.props.onDoubleClick && this.props.onDoubleClick()
- console.log("open options")
- this.props.setIsOpen(true)
- setTimeout(() => this.isDouble = false, 300)
- })}
- onClick={action((e) => {
- this.props.setIsOpen(!this.props.isOpen)
- })}
- style={{
- background: this.props.isOpen ? this.variantColor : this.backgroundColor,
- color: this.color
- }}>
- {this.props.title}
- <div className="propertiesView-sectionTitle-icon">
- <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" />
- </div>
- </div>
- {!this.props.isOpen ? null :
- <div className="propertiesView-content">
- {this.props.content}
- </div>
- }
- </div>
+ if (this.props.content === undefined || this.props.content === null) return null;
+ else
+ return (
+ <div className="propertiesView-section" onPointerEnter={action(() => this.props.setInSection && this.props.setInSection(true))} onPointerLeave={action(() => this.props.setInSection && this.props.setInSection(false))}>
+ <div
+ className="propertiesView-sectionTitle"
+ onDoubleClick={action(e => {
+ this.isDouble = true;
+ this.props.onDoubleClick && this.props.onDoubleClick();
+ this.props.setIsOpen(true);
+ setTimeout(() => (this.isDouble = false), 300);
+ })}
+ onClick={action(e => {
+ this.props.setIsOpen(!this.props.isOpen);
+ })}
+ style={{
+ background: this.props.isOpen ? this.variantColor : this.backgroundColor,
+ color: this.color,
+ }}>
+ {this.props.title}
+ <div className="propertiesView-sectionTitle-icon">
+ <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" />
+ </div>
+ </div>
+ {!this.props.isOpen ? null : <div className="propertiesView-content">{this.props.content}</div>}
+ </div>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 27b9c3c7a..872f1c6ab 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -8,7 +8,7 @@ import { concat } from 'lodash';
import { Lambda, action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { ColorState, SketchPicker } from 'react-color';
-import * as Icons from "react-icons/bs"; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import * as Icons from 'react-icons/bs'; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
import { GrCircleInformation } from 'react-icons/gr';
import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
@@ -51,7 +51,6 @@ interface PropertiesViewProps {
addDocTab: (doc: Doc, where: OpenWhere) => boolean;
}
-
@observer
export class PropertiesView extends React.Component<PropertiesViewProps> {
private _widthUndo?: UndoManager.Batch;
@@ -191,7 +190,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
});
rows.push(
- <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: "white", textAlign: "center" }}>
+ <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: 'white', textAlign: 'center' }}>
<EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
</div>
);
@@ -249,14 +248,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return !this.selectedDoc ? null : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
}
- @computed get contextCount(){
- console.log("in context count");
- if (this.selectedDocumentView){
- const target = (this.selectedDocumentView.props.Document)
- const embeddings = DocListCast(target.proto_embeddings)
- console.log(embeddings.length -1 );
- return (embeddings.length - 1)
- } else{
+ @computed get contextCount() {
+ if (this.selectedDocumentView) {
+ const target = this.selectedDocumentView.props.Document;
+ const embeddings = DocListCast(target.proto_embeddings);
+ return embeddings.length - 1;
+ } else {
return 0;
}
}
@@ -266,13 +263,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return !selAnchor ? null : <PropertiesDocBacklinksSelector Document={selAnchor} hideTitle={true} addDocTab={this.props.addDocTab} />;
}
- @computed get linkCount(){
- const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
+ @computed get linkCount() {
+ const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
var counter = 0;
- LinkManager.Links(selAnchor).forEach((l, i) =>
- counter ++
- );
+ LinkManager.Links(selAnchor).forEach((l, i) => counter++);
return counter;
}
@@ -356,7 +351,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return (
<Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
<div className="notify-button">
- <FontAwesomeIcon className="notify-button-icon" icon="bell" size="sm" />
+ <FontAwesomeIcon className="notify-button-icon" icon="bell" size="sm" />
</div>
</Tooltip>
);
@@ -367,16 +362,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
*/
@computed get expansionIcon() {
return (
- <div className="expansion-button" >
- <IconButton
- icon={<FontAwesomeIcon icon={'ellipsis-h'} />}
- size={Size.XSMALL}
- color={StrCast(Doc.UserDoc().userColor)}
+ <div className="expansion-button">
+ <IconButton
+ icon={<FontAwesomeIcon icon={'ellipsis-h'} />}
+ size={Size.XSMALL}
+ color={StrCast(Doc.UserDoc().userColor)}
onClick={action(() => {
if (this.selectedDocumentView || this.selectedDoc) {
SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
}
- })}
+ })}
/>
</div>
);
@@ -418,7 +413,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div>
<div className={'propertiesView-shareDropDown'}>
<div className={`propertiesView-shareDropDown${permission}`}>
- <div >{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
+ <div>{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
</div>
</div>
</div>
@@ -450,7 +445,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const target = docs[0];
const showAdmin = GetEffectiveAcl(target) == AclAdmin;
- console.log(GetEffectiveAcl(target), Doc.GetProto(target)[`acl-${normalizeEmail(Doc.CurrentUserEmail)}`])
const individualTableEntries = [];
const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
@@ -522,7 +516,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div>
<br></br> Individuals with Access to this Document
</div>
- <div className="propertiesView-sharingTable" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ <div className="propertiesView-sharingTable" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
{<div> {individualTableEntries}</div>}
</div>
{groupTableEntries.length > 0 ? (
@@ -530,7 +524,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div>
<br></br> Groups with Access to this Document
</div>
- <div className="propertiesView-sharingTable" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+ <div className="propertiesView-sharingTable" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
{<div> {groupTableEntries}</div>}
</div>
</div>
@@ -562,68 +556,43 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
@computed get editableTitle() {
-
const titles = new Set<string>();
const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
- return (
- <EditableText
- val={title}
- setVal={this.setTitle}
- color={this.color}
- type={Type.SEC}
- formLabel={"Title"}
- fillWidth
- />
- );
+ return <EditableText val={title} setVal={this.setTitle} color={this.color} type={Type.SEC} formLabel={'Title'} fillWidth />;
}
@computed get currentType() {
- // console.log("current type " + this.selectedDoc?.type)
-
- const documentType = StrCast(this.selectedDoc?.type)
- var currentType: string = documentType;
- var capitalizedDocType = Utils.cleanDocumentType(currentType as DocumentType);
-
- return (
- <div>
- <div className = "propertiesView-wordType">Type</div>
- <div className= "currentType">
- <div className='currentType-icon'>
- {this.currentComponent}
- </div>
-
- {capitalizedDocType}
-
- </div>
-
- </div>
-
- )
- }
+ const documentType = StrCast(this.selectedDoc?.type);
+ var currentType: string = documentType;
+ var capitalizedDocType = Utils.cleanDocumentType(currentType as DocumentType);
- @computed get currentComponent() {
+ return (
+ <div>
+ <div className="propertiesView-wordType">Type</div>
+ <div className="currentType">
+ <div className="currentType-icon">{this.currentComponent}</div>
- var iconName = StrCast(this.selectedDoc?.systemIcon)
+ {capitalizedDocType}
+ </div>
+ </div>
+ );
+ }
- // if (this.selectedDoc?.type === DocumentType.COL){
- // console.log("i did it!")
- // }
-
+ @computed get currentComponent() {
+ var iconName = StrCast(this.selectedDoc?.systemIcon);
- if (iconName){
+ if (iconName) {
const Icon = Icons[iconName as keyof typeof Icons];
return <Icon />;
- } else{
- return <Icons.BsFillCollectionFill/>
-
+ } else {
+ return <Icons.BsFillCollectionFill />;
}
}
@undoBatch
@action
setTitle = (value: string | number) => {
- console.log(value)
if (SelectionManager.Views().length > 1) {
SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true));
} else if (this.dataDoc) {
@@ -687,7 +656,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
className="inking-button-points"
style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? 'black' : '' }}
onPointerDown={action(() => (InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton))}>
- <FontAwesomeIcon icon="bezier-curve" size="lg" />
+ <FontAwesomeIcon icon="bezier-curve" size="lg" />
</div>
</Tooltip>
</div>
@@ -706,10 +675,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} onKeyPress={e => e.stopPropagation()} />
<div className="inputBox-button">
<div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
- <FontAwesomeIcon icon="caret-up" size="sm" />
+ <FontAwesomeIcon icon="caret-up" size="sm" />
</div>
<div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
- <FontAwesomeIcon icon="caret-down" size="sm" />
+ <FontAwesomeIcon icon="caret-down" size="sm" />
</div>
</div>
</div>
@@ -963,10 +932,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} />
<div className="inputBox-button">
<div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
- <FontAwesomeIcon icon="caret-up" size="sm" />
+ <FontAwesomeIcon icon="caret-up" size="sm" />
</div>
<div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
- <FontAwesomeIcon icon="caret-down" size="sm" />
+ <FontAwesomeIcon icon="caret-down" size="sm" />
</div>
</div>
</div>
@@ -983,7 +952,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
this.openSharing = false;
this.openLayout = false;
this.openFilters = false;
- }
+ };
@computed get widthAndDash() {
return (
@@ -1065,64 +1034,45 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: any) => {
- return <div>
- <NumberInput
- formLabel={label}
- formLabelPlacement={'left'}
- type={Type.SEC}
- unit={unit}
- fillWidth
- color={this.color}
- number={number}
- setNumber={setNumber}
- min={min}
- max={max}
- />
- <Slider
- multithumb={false}
- color={this.color}
- size={Size.XSMALL}
- min={min}
- max={max}
- unit={unit}
- number={number}
- setNumber={setNumber}
- fillWidth
- />
- </div>
- }
+ return (
+ <div>
+ <NumberInput formLabel={label} formLabelPlacement={'left'} type={Type.SEC} unit={unit} fillWidth color={this.color} number={number} setNumber={setNumber} min={min} max={max} />
+ <Slider multithumb={false} color={this.color} size={Size.XSMALL} min={min} max={max} unit={unit} number={number} setNumber={setNumber} fillWidth />
+ </div>
+ );
+ };
@computed get transformEditor() {
return (
<div className="transform-editor">
{this.isInk ? this.controlPointsButton : null}
{this.getNumber(
- "Height",
- " px",
+ 'Height',
+ ' px',
0,
1000,
Number(this.shapeHgt),
undoable((val: string) => !isNaN(Number(val)) && (this.shapeHgt = val), 'set height')
)}
{this.getNumber(
- "Width",
- " px",
+ 'Width',
+ ' px',
0,
1000,
Number(this.shapeWid),
undoable((val: string) => !isNaN(Number(val)) && (this.shapeWid = val), 'set width')
)}
{this.getNumber(
- "X Coordinate",
- " px",
+ 'X Coordinate',
+ ' px',
-2000,
2000,
Number(this.shapeXps),
undoable((val: string) => !isNaN(Number(val)) && (this.shapeXps = val), 'set x coord')
)}
{this.getNumber(
- "Y Coordinate",
- " px",
+ 'Y Coordinate',
+ ' px',
-2000,
2000,
Number(this.shapeYps),
@@ -1133,38 +1083,44 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
@computed get optionsSubMenu() {
- return <PropertiesSection
- title="Options"
- content={<PropertiesButtons />}
- inSection={this.inOptions}
- isOpen={this.openOptions}
- setInSection={(bool) => this.inOptions = bool}
- setIsOpen={(bool) => this.openOptions = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return (
+ <PropertiesSection
+ title="Options"
+ content={<PropertiesButtons />}
+ inSection={this.inOptions}
+ isOpen={this.openOptions}
+ setInSection={bool => (this.inOptions = bool)}
+ setIsOpen={bool => (this.openOptions = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
@computed get sharingSubMenu() {
- return <PropertiesSection
- title="Sharing & Permissions"
- content={<>
- {/* <div className="propertiesView-buttonContainer"> */}
- <div className="propertiesView-acls-checkbox">
- Layout Permissions
- <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
- </div>
- {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+ return (
+ <PropertiesSection
+ title="Sharing & Permissions"
+ content={
+ <>
+ {/* <div className="propertiesView-buttonContainer"> */}
+ <div className="propertiesView-acls-checkbox">
+ Layout Permissions
+ <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
+ </div>
+ {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
<button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
<FontAwesomeIcon icon="redo-alt" size="1x" />
</button>
</Tooltip> */}
- {/* </div> */}
- {this.sharingTable}
- </>}
- isOpen={this.openSharing}
- setIsOpen={(bool) => this.openSharing = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ {/* </div> */}
+ {this.sharingTable}
+ </>
+ }
+ isOpen={this.openSharing}
+ setIsOpen={bool => (this.openSharing = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
/**
@@ -1192,15 +1148,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
};
@computed get filtersSubMenu() {
- return <PropertiesSection
- title="Filters"
- content={<div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
- <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
- </div>}
- isOpen={this.openFilters}
- setIsOpen={(bool) => this.openFilters = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return (
+ <PropertiesSection
+ title="Filters"
+ content={
+ <div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
+ <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
+ </div>
+ }
+ isOpen={this.openFilters}
+ setIsOpen={bool => (this.openFilters = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
@computed get inkSubMenu() {
@@ -1208,68 +1168,42 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return (
<>
- <PropertiesSection
- title="Appearance"
- content={this.isInk ? this.appearanceEditor : null}
- isOpen={this.openAppearance}
- setIsOpen={(bool) => this.openAppearance = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
- <PropertiesSection
- title="Transform"
- content={this.transformEditor}
- isOpen={this.openTransform}
- setIsOpen={(bool) => this.openTransform = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ <PropertiesSection title="Appearance" content={this.isInk ? this.appearanceEditor : null} isOpen={this.openAppearance} setIsOpen={bool => (this.openAppearance = bool)} onDoubleClick={() => this.onDoubleClick()} />
+ <PropertiesSection title="Transform" content={this.transformEditor} isOpen={this.openTransform} setIsOpen={bool => (this.openTransform = bool)} onDoubleClick={() => this.onDoubleClick()} />
</>
);
}
@computed get fieldsSubMenu() {
- return <PropertiesSection
- title="Fields & Tags"
- content={<div className="propertiesView-content fields">{
- Doc.noviceMode ? this.noviceFields : this.expandedField}
- </div>}
- isOpen={this.openFields}
- setIsOpen={(bool) => this.openFields = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return (
+ <PropertiesSection
+ title="Fields & Tags"
+ content={<div className="propertiesView-content fields">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>}
+ isOpen={this.openFields}
+ setIsOpen={bool => (this.openFields = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
@computed get contextsSubMenu() {
- return <PropertiesSection
- title="Other Contexts"
- content={this.contextCount > 0 ? this.contexts : "There are no other contexts."}
- isOpen={this.openContexts}
- setIsOpen={(bool) => this.openContexts = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return (
+ <PropertiesSection
+ title="Other Contexts"
+ content={this.contextCount > 0 ? this.contexts : 'There are no other contexts.'}
+ isOpen={this.openContexts}
+ setIsOpen={bool => (this.openContexts = bool)}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ );
}
-
-
-
-
@computed get linksSubMenu() {
- return <PropertiesSection
- title="Linked To"
- content={this.linkCount > 0 ? this.links : "There are no current links." }
- isOpen={this.openLinks}
- setIsOpen={(bool) => this.openLinks = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return <PropertiesSection title="Linked To" content={this.linkCount > 0 ? this.links : 'There are no current links.'} isOpen={this.openLinks} setIsOpen={bool => (this.openLinks = bool)} onDoubleClick={() => this.onDoubleClick()} />;
}
@computed get layoutSubMenu() {
- return <PropertiesSection
- title="Layout"
- content={this.layoutPreview}
- isOpen={this.openLayout}
- setIsOpen={(bool) => this.openLayout = bool}
- onDoubleClick={() => this.onDoubleClick()}
- />
+ return <PropertiesSection title="Layout" content={this.layoutPreview} isOpen={this.openLayout} setIsOpen={bool => (this.openLayout = bool)} onDoubleClick={() => this.onDoubleClick()} />;
}
@computed get description() {
@@ -1470,224 +1404,226 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
if (scale > 1) scale = 1;
this.sourceAnchor && (this.sourceAnchor.followLinkZoomScale = scale);
};
-
+
@computed get linkProperties() {
const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3));
const targZoom = this.sourceAnchor?.followLinkZoom;
const indent = 30;
const hasSelectedAnchor = LinkManager.Links(this.sourceAnchor).includes(LinkManager.currentLink!);
- return <>
- <div className="propertiesView-section" style={{ background: 'darkgray' }}>
- <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
- <p>Relationship</p>
- {this.editRelationship}
- </div>
- <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
- <p>Description</p>
- {this.editDescription}
- </div>
- <div className="propertiesView-input inline">
- <p>Show link</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
- <p>Auto-move anchors</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
- <p>Display arrow</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- </div>
- {!hasSelectedAnchor ? null : (
- <div className="propertiesView-section">
- <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
- <p>Follow by</p>
- <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
- <option value={undefined}>Default</option>
- <option value={OpenWhere.addLeft}>Opening in new left pane</option>
- <option value={OpenWhere.addRight}>Opening in new right pane</option>
- <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
- <option value={OpenWhere.replaceRight}>Replacing right tab</option>
- <option value={OpenWhere.lightbox}>Opening in lightbox</option>
- <option value={OpenWhere.add}>Opening in new tab</option>
- <option value={OpenWhere.replace}>Replacing current tab</option>
- <option value={OpenWhere.inParent}>Opening in same collection</option>
- {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
- </select>
- </div>
- <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
- <p>Animation</p>
- <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
- <option value="default">Default</option>
- {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
- <option key={effect.toString()} value={effect.toString()}>
- {effect.toString()}
- </option>
- ))}
- </select>
- <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
- {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
- {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
- {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
- {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
- {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+ return (
+ <>
+ <div className="propertiesView-section" style={{ background: 'darkgray' }}>
+ <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
+ <p>Relationship</p>
+ {this.editRelationship}
+ </div>
+ <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
+ <p>Description</p>
+ {this.editDescription}
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Show link</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
+ <p>Auto-move anchors</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
+ <p>Display arrow</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
</div>
</div>
- {PresBox.inputter(
- '0.1',
- '0.1',
- '10',
- NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
- true,
- (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
- indent
- )}{' '}
- <div
- className={'slider-headers'}
- style={{
- display: 'grid',
- justifyContent: 'space-between',
- width: `calc(100% - ${indent * 2}px)`,
- marginLeft: indent,
- marginRight: indent,
- gridTemplateColumns: 'auto auto',
- borderTop: 'solid',
- }}>
- <div className="slider-text">Fast</div>
- <div className="slider-text">Slow</div>
- </div>{' '}
- <div className="propertiesView-input inline">
- <p>Play Target Audio</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Zoom Text Selections</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Toggle Follow to Outer Context</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Toggle Target (Show/Hide)</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Ease Transitions</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Capture Offset to Target</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => {
- this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
- this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
- }}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Center Target (no zoom)</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
- <p>Zoom %</p>
- <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
- <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
- <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
- <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
- <FontAwesomeIcon icon={'caret-up'} />
+ {!hasSelectedAnchor ? null : (
+ <div className="propertiesView-section">
+ <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
+ <p>Follow by</p>
+ <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
+ <option value={undefined}>Default</option>
+ <option value={OpenWhere.addLeft}>Opening in new left pane</option>
+ <option value={OpenWhere.addRight}>Opening in new right pane</option>
+ <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
+ <option value={OpenWhere.replaceRight}>Replacing right tab</option>
+ <option value={OpenWhere.lightbox}>Opening in lightbox</option>
+ <option value={OpenWhere.add}>Opening in new tab</option>
+ <option value={OpenWhere.replace}>Replacing current tab</option>
+ <option value={OpenWhere.inParent}>Opening in same collection</option>
+ {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
+ </select>
+ </div>
+ <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
+ <p>Animation</p>
+ <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
+ <option value="default">Default</option>
+ {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
+ <option key={effect.toString()} value={effect.toString()}>
+ {effect.toString()}
+ </option>
+ ))}
+ </select>
+ <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
+ {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+ {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+ {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+ {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+ {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
</div>
- <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
- <FontAwesomeIcon icon={'caret-down'} />
+ </div>
+ {PresBox.inputter(
+ '0.1',
+ '0.1',
+ '10',
+ NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
+ true,
+ (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
+ indent
+ )}{' '}
+ <div
+ className={'slider-headers'}
+ style={{
+ display: 'grid',
+ justifyContent: 'space-between',
+ width: `calc(100% - ${indent * 2}px)`,
+ marginLeft: indent,
+ marginRight: indent,
+ gridTemplateColumns: 'auto auto',
+ borderTop: 'solid',
+ }}>
+ <div className="slider-text">Fast</div>
+ <div className="slider-text">Slow</div>
+ </div>{' '}
+ <div className="propertiesView-input inline">
+ <p>Play Target Audio</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Zoom Text Selections</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Toggle Follow to Outer Context</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Toggle Target (Show/Hide)</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Ease Transitions</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Capture Offset to Target</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => {
+ this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
+ this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
+ }}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Center Target (no zoom)</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
+ <p>Zoom %</p>
+ <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
+ <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
+ <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
+ <FontAwesomeIcon icon={'caret-up'} />
+ </div>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
+ <FontAwesomeIcon icon={'caret-down'} />
+ </div>
+ </div>
</div>
+ <button
+ style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
</div>
+ {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
+ <div
+ className={'slider-headers'}
+ style={{
+ display: !targZoom ? 'none' : 'grid',
+ justifyContent: 'space-between',
+ width: `calc(100% - ${indent * 2}px)`,
+ marginLeft: indent,
+ marginRight: indent,
+ gridTemplateColumns: 'auto auto',
+ borderTop: 'solid',
+ }}>
+ <div className="slider-text">0%</div>
+ <div className="slider-text">100%</div>
+ </div>{' '}
</div>
- <button
- style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
- <div
- className={'slider-headers'}
- style={{
- display: !targZoom ? 'none' : 'grid',
- justifyContent: 'space-between',
- width: `calc(100% - ${indent * 2}px)`,
- marginLeft: indent,
- marginRight: indent,
- gridTemplateColumns: 'auto auto',
- borderTop: 'solid',
- }}>
- <div className="slider-text">0%</div>
- <div className="slider-text">100%</div>
- </div>{' '}
- </div>
- )}
- </>
+ )}
+ </>
+ );
}
/**
@@ -1723,23 +1659,20 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
width: this.props.width,
minWidth: this.props.width,
}}>
- <div className = "propertiesView-propAndInfoGrouping">
+ <div className="propertiesView-propAndInfoGrouping">
<div className="propertiesView-title" style={{ width: this.props.width }}>
Properties
</div>
- <div className = "propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/')}>
- <GrCircleInformation/> </div>
-
+ <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/')}>
+ <GrCircleInformation />{' '}
+ </div>
</div>
-
<div className="propertiesView-name">{this.editableTitle}</div>
- <div className = "propertiesView-type"> {this.currentType} </div>
+ <div className="propertiesView-type"> {this.currentType} </div>
{this.contextsSubMenu}
{this.linksSubMenu}
- {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : (
- this.linkProperties
- )}
+ {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : this.linkProperties}
{this.inkSubMenu}
{this.optionsSubMenu}
{this.fieldsSubMenu}
@@ -1760,7 +1693,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
Presentation
</div>
<div className="propertiesView-name" style={{ borderBottom: 0 }}>
-
{this.editableTitle}
<div className="propertiesView-presSelected">
<div className="propertiesView-selectedCount">{PresBox.Instance.selectedArray.size} selected</div>
@@ -1772,7 +1704,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Transitions
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
+ <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
@@ -1786,7 +1718,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Visibilty
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
+ <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresVisibilityAndDuration ? <div className="propertiesView-presTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
@@ -1797,7 +1729,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Progressivize
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
+ <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresProgressivize ? <div className="propertiesView-presTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
@@ -1808,7 +1740,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
+ <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openSlideOptions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index bbbad3690..63ff348e3 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -22,7 +22,7 @@ import { DocumentViewProps } from './nodes/DocumentView';
import { FieldViewProps } from './nodes/FieldView';
import { KeyValueBox } from './nodes/KeyValueBox';
import { SliderBox } from './nodes/SliderBox';
-import { BsArrowDown, BsArrowUp, BsArrowDownUp } from 'react-icons/bs'
+import { BsArrowDown, BsArrowUp, BsArrowDownUp } from 'react-icons/bs';
import './StyleProvider.scss';
import React = require('react');
@@ -44,7 +44,6 @@ export enum StyleProp {
ShowCaption = 'layout_showCaption',
TitleHeight = 'titleHeight', // Height of Title area
ShowTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix)
- JitterRotation = 'jitterRotation', // whether documents should be randomly rotated
BorderPath = 'customBorder', // border path for document view
FontSize = 'fontSize', // size of text font
FontFamily = 'fontFamily', // font family of text
@@ -98,7 +97,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
const layout_showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
- const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
// prettier-ignore
switch (property.split(':')[0]) {
case StyleProp.TreeViewIcon:
@@ -161,7 +159,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
''
);
case StyleProp.Color:
- if (MainView.Instance.LastButton === doc) return Doc.UserDoc().userBackgroundColor;
+ if (MainView.Instance.LastButton === doc) return SettingsManager.Instance.userBackgroundColor;
if (Doc.IsSystem(doc!)) return StrCast(Doc.UserDoc().userColor)
if (doc?.type === DocumentType.FONTICON) return Doc.UserDoc().userColor;
const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color));
@@ -188,8 +186,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
</div>
),
};
- case StyleProp.JitterRotation:
- return Doc.IsComicStyle(doc) ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
case StyleProp.HeaderMargin:
return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._type_collection as any) ||
(doc?.type === DocumentType.RTF && !layout_showTitle()?.includes('noMargin')) ||
@@ -208,12 +204,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case DocumentType.PRES: docColor = docColor || (darkScheme() ? 'transparent' : 'transparent'); break;
case DocumentType.FONTICON: docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
- case DocumentType.FILTER: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : 'rgba(105, 105, 105, 0.432)'); break;
case DocumentType.INK: docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
case DocumentType.EQUATION: docColor = docColor || 'transparent'; break;
case DocumentType.LABEL: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
- case DocumentType.LINKANCHOR: docColor = isAnchor ? Colors.LIGHT_BLUE : 'transparent'; break;
case DocumentType.LINK: docColor = (isAnchor ? docColor : '') || 'transparent'; break;
case DocumentType.IMG:
case DocumentType.WEB:
@@ -224,7 +218,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case DocumentType.COL:
if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
docColor = docColor || (Doc.IsSystem(doc)
- ? StrCast(Doc.UserDoc().userBackgroundColor)
+ ? SettingsManager.Instance.userBackgroundColor
: doc.annotationOn
? '#00000010' // faint interior for collections on PDFs, images, etc
: doc?._isGroup
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index caf04cc1b..a551e5332 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -7,6 +7,7 @@ import { StrCast } from '../../fields/Types';
import { Doc } from '../../fields/Doc';
import { Popup, Type, isDark } from 'browndash-components';
import { Colors } from './global/globalEnums';
+import { SettingsManager } from '../util/SettingsManager';
interface UndoStackProps {
width?: number;
@@ -18,35 +19,37 @@ export class UndoStack extends React.Component<UndoStackProps> {
@observable static HideInline: boolean;
@observable static Expand: boolean;
render() {
- const background = UndoManager.batchCounter.get() ? 'yellow' : StrCast(Doc.UserDoc().userBackgroundColor)
+ const background = UndoManager.batchCounter.get() ? 'yellow' : SettingsManager.Instance.userBackgroundColor;
return this.props.inline && UndoStack.HideInline ? null : (
<div className="undoStack-outerContainer">
- <Popup
+ <Popup
text={'Undo/Redo Stack'}
color={UndoManager.batchCounter.get() ? 'yellow' : StrCast(Doc.UserDoc().userVariantColor)}
placement={`top-start`}
type={Type.TERT}
popup={
- <div className="undoStack-commandsContainer" ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })}
- style={{
- background: background,
- color: isDark(background) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY
- }}>
- {UndoManager.undoStackNames.map((name, i) => (
- <div className="undoStack-resultContainer" key={i}>
- <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
- </div>
- ))}
- {Array.from(UndoManager.redoStackNames)
- .reverse()
- .map((name, i) => (
+ <div
+ className="undoStack-commandsContainer"
+ ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })}
+ style={{
+ background: background,
+ color: isDark(background) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY,
+ }}>
+ {UndoManager.undoStackNames.map((name, i) => (
<div className="undoStack-resultContainer" key={i}>
- <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
- {name.replace(/[^\.]*\./, '')}
- </div>
+ <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
</div>
- ))}
- </div>
+ ))}
+ {Array.from(UndoManager.redoStackNames)
+ .reverse()
+ .map((name, i) => (
+ <div className="undoStack-resultContainer" key={i}>
+ <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
+ {name.replace(/[^\.]*\./, '')}
+ </div>
+ </div>
+ ))}
+ </div>
}
/>
</div>
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index 3465a5283..addc00c85 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -123,7 +123,7 @@ export type RegionData = makeInterface<[typeof RegionDataSchema]>;
export const RegionData = makeInterface(RegionDataSchema);
interface IProps {
- node: Doc;
+ animatedDoc: Doc;
RegionData: Doc;
collection: Doc;
tickSpacing: number;
@@ -167,7 +167,7 @@ export class Keyframe extends React.Component<IProps> {
return RegionData(this.props.RegionData);
}
@computed private get regions() {
- return DocListCast(this.props.node.regions);
+ return DocListCast(this.props.animatedDoc.regions);
}
@computed private get keyframes() {
return DocListCast(this.regiondata.keyframes);
@@ -375,7 +375,7 @@ export class Keyframe extends React.Component<IProps> {
*/
@action
makeRegionMenu = (kf: Doc, e: MouseEvent) => {
- TimelineMenu.Instance.addItem('button', 'Remove Region', () => Cast(this.props.node.regions, listSpec(Doc))?.splice(this.regions.indexOf(this.props.RegionData), 1)),
+ TimelineMenu.Instance.addItem('button', 'Remove Region', () => Cast(this.props.animatedDoc.regions, listSpec(Doc))?.splice(this.regions.indexOf(this.props.RegionData), 1)),
TimelineMenu.Instance.addItem('input', `fadeIn: ${this.regiondata.fadeIn}ms`, val => {
runInAction(() => {
let cannotMove: boolean = false;
@@ -461,7 +461,7 @@ export class Keyframe extends React.Component<IProps> {
e.stopPropagation();
const div = ref.current!;
div.style.opacity = '1';
- Doc.BrushDoc(this.props.node);
+ Doc.BrushDoc(this.props.animatedDoc);
};
/**
@@ -473,7 +473,7 @@ export class Keyframe extends React.Component<IProps> {
e.stopPropagation();
const div = ref.current!;
div.style.opacity = '0';
- Doc.UnBrushDoc(this.props.node);
+ Doc.UnBrushDoc(this.props.animatedDoc);
};
///////////////////////UI STUFF /////////////////////////
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index adc97bbb4..7ca13756a 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -535,7 +535,7 @@ export class Timeline extends React.Component<FieldViewProps> {
{this.children.map(doc => (
<Track
ref={ref => this.mapOfTracks.push(ref)}
- node={doc}
+ animatedDoc={doc}
currentBarX={this._currentBarX}
changeCurrentBarX={this.changeCurrentBarX}
transform={this.props.ScreenToLocalTransform()}
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 2349ba786..1010332f5 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -12,7 +12,7 @@ import { Keyframe, KeyframeFunc, RegionData } from './Keyframe';
import './Track.scss';
interface IProps {
- node: Doc;
+ animatedDoc: Doc;
currentBarX: number;
transform: Transform;
collection: Doc;
@@ -36,23 +36,23 @@ export class Track extends React.Component<IProps> {
private objectWhitelist = ['data'];
@computed private get regions() {
- return DocListCast(this.props.node.regions);
+ return DocListCast(this.props.animatedDoc.regions);
}
@computed private get time() {
return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement));
}
async componentDidMount() {
- const regions = await DocListCastAsync(this.props.node.regions);
- if (!regions) this.props.node.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff
+ const regions = await DocListCastAsync(this.props.animatedDoc.regions);
+ if (!regions) this.props.animatedDoc.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff
//these two lines are exactly same from timeline.tsx
const relativeHeight = window.innerHeight / 20;
runInAction(() => (this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT)); //for responsiveness
this._timelineVisibleReaction = this.timelineVisibleReaction();
this._currentBarXReaction = this.currentBarXReaction();
- if (DocListCast(this.props.node.regions).length === 0) this.createRegion(this.time);
- this.props.node.hidden = false;
- this.props.node.opacity = 1;
+ if (DocListCast(this.props.animatedDoc.regions).length === 0) this.createRegion(this.time);
+ this.props.animatedDoc.hidden = false;
+ this.props.animatedDoc.opacity = 1;
// this.autoCreateKeyframe();
}
@@ -127,13 +127,13 @@ export class Track extends React.Component<IProps> {
*/
@action
autoCreateKeyframe = () => {
- const objects = this.objectWhitelist.map(key => this.props.node[key]);
- intercept(this.props.node, change => {
+ const objects = this.objectWhitelist.map(key => this.props.animatedDoc[key]);
+ intercept(this.props.animatedDoc, change => {
return change;
});
return reaction(
() => {
- return [...this.primitiveWhitelist.map(key => this.props.node[key]), ...objects];
+ return [...this.primitiveWhitelist.map(key => this.props.animatedDoc[key]), ...objects];
},
(changed, reaction) => {
//check for region
@@ -171,14 +171,14 @@ export class Track extends React.Component<IProps> {
() => {
const regiondata = this.findRegion(this.time);
if (regiondata) {
- this.props.node.hidden = false;
+ this.props.animatedDoc.hidden = false;
// if (!this._autoKfReaction) {
// // this._autoKfReaction = this.autoCreateKeyframe();
// }
this.timeChange();
} else {
- this.props.node.hidden = true;
- this.props.node.opacity = 0;
+ this.props.animatedDoc.hidden = true;
+ this.props.animatedDoc.opacity = 0;
//if (this._autoKfReaction) this._autoKfReaction();
}
}
@@ -250,10 +250,10 @@ export class Track extends React.Component<IProps> {
private applyKeys = async (kf: Doc) => {
this.primitiveWhitelist.forEach(key => {
if (!kf[key]) {
- this.props.node[key] = undefined;
+ this.props.animatedDoc[key] = undefined;
} else {
const stored = kf[key];
- this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
+ this.props.animatedDoc[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
}
});
};
@@ -282,11 +282,11 @@ export class Track extends React.Component<IProps> {
const dif = NumCast(right[key]) - NumCast(left[key]);
const deltaLeft = this.time - NumCast(left.time);
const ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time));
- this.props.node[key] = NumCast(left[key]) + dif * ratio;
+ this.props.animatedDoc[key] = NumCast(left[key]) + dif * ratio;
} else {
// case data
const stored = left[key];
- this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
+ this.props.animatedDoc[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
}
});
};
@@ -326,7 +326,7 @@ export class Track extends React.Component<IProps> {
regiondata.duration = rightRegion.position - regiondata.position;
}
if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) {
- Cast(this.props.node.regions, listSpec(Doc))?.push(regiondata);
+ Cast(this.props.animatedDoc.regions, listSpec(Doc))?.push(regiondata);
this._newKeyframe = true;
this.saveStateRegion = regiondata;
return regiondata;
@@ -360,7 +360,7 @@ export class Track extends React.Component<IProps> {
@action
copyDocDataToKeyFrame = (doc: Doc) => {
this.primitiveWhitelist.map(key => {
- const originalVal = this.props.node[key];
+ const originalVal = this.props.animatedDoc[key];
doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal;
});
};
@@ -377,8 +377,8 @@ export class Track extends React.Component<IProps> {
ref={this._inner}
style={{ height: `${this._trackHeight}px` }}
onDoubleClick={this.onInnerDoubleClick}
- onPointerOver={() => Doc.BrushDoc(this.props.node)}
- onPointerOut={() => Doc.UnBrushDoc(this.props.node)}>
+ onPointerOver={() => Doc.BrushDoc(this.props.animatedDoc)}
+ onPointerOut={() => Doc.UnBrushDoc(this.props.animatedDoc)}>
{this.regions?.map((region, i) => {
return <Keyframe key={`${i}`} {...this.props} RegionData={region} makeKeyData={this.makeKeyData} />;
})}
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index a4c5229aa..d93015506 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -176,6 +176,7 @@
display: flex;
align-content: center;
justify-content: center;
+ background: $dark-gray;
}
.lm_controls > li {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 16982595d..0052c4196 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -164,7 +164,7 @@ export class CollectionDockingView extends CollectionSubView() {
public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
if (document?._type_collection === CollectionViewType.Docking && !keyValue) return DashboardView.openDashboard(document);
if (!CollectionDockingView.Instance) return false;
- const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !keyValue);
+ const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !tab.contentItem.config.props.keyValue && !keyValue);
if (tab) {
tab.header.parent.setActiveContentItem(tab.contentItem);
return true;
@@ -466,7 +466,10 @@ export class CollectionDockingView extends CollectionSubView() {
this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
if (tab.DashDoc && ![DocumentType.PRES].includes(tab.DashDoc?.type) && !tab.contentItem.config.props.keyValue) {
Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
- Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
+ // if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
+ if (tab.DashDoc.embedContainer === this.rootDoc) tab.DashDoc.embedContainer = undefined;
+ if (!tab.DashDoc.embedContainer) Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
+ Doc.RemoveDocFromList(Doc.GetProto(tab.DashDoc), 'proto_embeddings', tab.DashDoc);
}
if (CollectionDockingView.Instance) {
const dview = CollectionDockingView.Instance.props.Document;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 6f88f6727..06522b85e 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -7,8 +7,7 @@ import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
-import { NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, numberRange, returnEmptyString, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction, numberRange, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { CompileScript } from '../../util/Scripting';
@@ -252,10 +251,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
return (
<div className="collectionStackingView-optionPicker">
<div className="optionOptions">
- <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.toggleEmbedding}>
- Create Embedding
- </div>
- <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.deleteRow}>
+ <div className={'optionPicker' + (selected === true ? ' active' : '')} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.deleteRow)}>
Delete
</div>
</div>
@@ -273,13 +269,16 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
const stackPad = showChrome ? `0px ${this.props.parent.xMargin}px` : `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px 0px ${this.props.parent.xMargin}px `;
return this.collapsed ? null : (
<div style={{ position: 'relative' }}>
+ {this.props.showHandle && this.props.parent.props.isContentActive() ? this.props.parent.columnDragger : null}
{showChrome ? (
<div
className="collectionStackingView-addDocumentButton"
- style={{
- //width: style.columnWidth / style.numGroupColumns,
- padding: `${NumCast(this.props.parent.layoutDoc._yPadding, this.props.parent.yMargin)}px 0px 0px 0px`,
- }}>
+ style={
+ {
+ //width: style.columnWidth / style.numGroupColumns,
+ //padding: `${NumCast(this.props.parent.layoutDoc._yPadding, this.props.parent.yMargin)}px 0px 0px 0px`,
+ }
+ }>
<EditableView GetValue={returnEmptyString} SetValue={this.addDocument} textCallback={this.textCallback} contents={'+ NEW'} />
</div>
) : null}
@@ -288,12 +287,12 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
ref={this._contRef}
style={{
padding: stackPad,
+ minHeight: this.props.showHandle && this.props.parent.props.isContentActive() ? '10px' : undefined,
width: this.props.parent.NodeWidth,
gridGap: this.props.parent.gridGap,
gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ''),
}}>
{this.props.parent.children(this.props.docList)}
- {this.props.showHandle && this.props.parent.props.isContentActive() ? this.props.parent.columnDragger : null}
</div>
</div>
);
@@ -313,23 +312,33 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
onPointerDown={this.headerDown}
title={evContents === `NO ${key.toUpperCase()} VALUE` ? `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ''}
style={{ background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this.color : 'lightgrey' }}>
- {noChrome ? evContents : editableHeaderView}
+ {noChrome ? evContents : <div>{editableHeaderView}</div>}
{noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? null : (
<div className="collectionStackingView-sectionColor">
- <button className="collectionStackingView-sectionColorButton" onClick={action(e => (this._paletteOn = !this._paletteOn))}>
+ <button
+ className="collectionStackingView-sectionColorButton"
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ action(e => (this._paletteOn = !this._paletteOn))
+ )
+ }>
<FontAwesomeIcon icon="palette" size="lg" />
</button>
{this._paletteOn ? this.renderColorPicker() : null}
</div>
)}
{noChrome ? null : (
- <button className="collectionStackingView-sectionDelete" onClick={noChrome ? undefined : this.collapseSection}>
+ <button className="collectionStackingView-sectionDelete" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, noChrome ? emptyFunction : this.collapseSection)}>
<FontAwesomeIcon icon={this.collapsed ? 'chevron-down' : 'chevron-up'} size="lg" />
</button>
)}
{noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? null : (
- <div className="collectionStackingView-sectionOptions">
- <Flyout anchorPoint={anchorPoints.TOP_CENTER} content={this.renderMenu()}>
+ <div className="collectionStackingView-sectionOptions" onPointerDown={e => e.stopPropagation()}>
+ <Flyout anchorPoint={anchorPoints.RIGHT_TOP} content={this.renderMenu()}>
<button className="collectionStackingView-sectionOptionButton">
<FontAwesomeIcon icon="ellipsis-v" size="lg" />
</button>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 9eb716763..f65e8698f 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -33,7 +33,7 @@ import { LightboxView } from '../LightboxView';
import { MainView } from '../MainView';
import { DefaultStyleProvider } from '../StyleProvider';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
-import { DocumentView, OpenWhereMod } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhereMod } from '../nodes/DocumentView';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
import { CollectionDockingView } from './CollectionDockingView';
import './CollectionMenu.scss';
@@ -129,7 +129,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
docViewPath={returnEmptyDoclist}
moveDocument={returnFalse}
addDocument={returnFalse}
- addDocTab={returnFalse}
+ addDocTab={DocumentViewInternal.addDocTabFunc}
pinToPres={emptyFunction}
removeDocument={returnFalse}
ScreenToLocalTransform={this.buttonBarXf}
@@ -154,21 +154,21 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
const hardCodedButtons = (
<div className={`hardCodedButtons`}>
- <Toggle
- toggleType={ToggleType.BUTTON}
- type={Type.PRIM}
- color={StrCast(Doc.UserDoc().userColor)}
- onClick={this.toggleTopBar}
- toggleStatus={SettingsManager.headerBarHeight > 0}
+ <Toggle
+ toggleType={ToggleType.BUTTON}
+ type={Type.PRIM}
+ color={StrCast(Doc.UserDoc().userColor)}
+ onClick={this.toggleTopBar}
+ toggleStatus={SettingsManager.headerBarHeight > 0}
icon={<FontAwesomeIcon icon={headerIcon} size="lg" />}
tooltip={headerTitle}
/>
- <Toggle
- toggleType={ToggleType.BUTTON}
+ <Toggle
+ toggleType={ToggleType.BUTTON}
type={Type.PRIM}
- color={StrCast(Doc.UserDoc().userColor)}
- onClick={this.toggleProperties}
- toggleStatus={SettingsManager.propertiesWidth > 0}
+ color={StrCast(Doc.UserDoc().userColor)}
+ onClick={this.toggleProperties}
+ toggleStatus={SettingsManager.propertiesWidth > 0}
icon={<FontAwesomeIcon icon={propIcon} size="lg" />}
tooltip={propTitle}
/>
@@ -178,11 +178,12 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
// NEW BUTTONS
//dash col linear view buttons
const contMenuButtons = (
- <div className="collectionMenu-container"
- style={{
- background: StrCast(Doc.UserDoc().userBackgroundColor),
- // borderColor: StrCast(Doc.UserDoc().userColor)
- }} >
+ <div
+ className="collectionMenu-container"
+ style={{
+ background: SettingsManager.Instance.userBackgroundColor,
+ // borderColor: StrCast(Doc.UserDoc().userColor)
+ }}>
{this.contMenuButtons}
{hardCodedButtons}
</div>
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 91be31289..bbd528e13 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -93,13 +93,14 @@ export class CollectionPileView extends CollectionSubView() {
this.layoutDoc._freeform_panY = -10;
this.props.Document._freeform_pileEngine = computePassLayout.name;
} else {
- const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500);
+ const defaultSize = NumCast(this.rootDoc._starburstDiameter, 400);
this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - defaultSize / 2;
this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - defaultSize / 2;
this.layoutDoc._freeform_pileWidth = this.layoutDoc[Width]();
this.layoutDoc._freeform_pileHeight = this.layoutDoc[Height]();
this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
this.layoutDoc._width = this.layoutDoc._height = defaultSize;
+ this.layoutDoc.background;
this.props.Document._freeform_pileEngine = computeStarburstLayout.name;
}
});
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 99a68e94b..255bc3889 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -128,6 +128,7 @@
height: 15;
position: absolute;
margin-left: -5;
+ z-index: 10;
}
// Documents in stacking view
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 805002452..e4a0d6dad 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,7 +1,7 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CursorProperty } from 'csstype';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Opt } from '../../../fields/Doc';
import { DocData, Height, Width } from '../../../fields/DocSymbols';
@@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { CollectionViewType } from '../../documents/DocumentTypes';
import { DragManager, dropActionType } from '../../util/DragManager';
@@ -31,7 +31,6 @@ import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
import './CollectionStackingView.scss';
import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
import { CollectionSubView } from './CollectionSubView';
-import { Colors } from '../global/globalEnums';
const _global = (window /* browser */ || global) /* node */ as any;
export type collectionStackingViewProps = {
@@ -59,7 +58,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// map of node headers to their heights. Used in Masonry
@observable _heightMap = new Map<string, number>();
// Assuming that this is the current css cursor style
- @observable _cursor: CursorProperty = 'grab';
+ @observable _cursor: CursorProperty = 'ew-resize';
// gets reset whenever we scroll. Not sure what it is
@observable _scroll = 0; // used to force the document decoration to update when scrolling
// does this mean whether the browser is hidden? Or is chrome something else entirely?
@@ -308,19 +307,18 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
? false
: undefined;
isChildButtonContentActive = () => (this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined);
+ @observable docRefs = new ObservableMap<Doc, DocumentView>();
// this is what renders the document that you see on the screen
// called in Children: this actually adds a document to our children list
getDisplayDoc(doc: Doc, width: () => number, count: number) {
const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc;
const height = () => this.getDocHeight(doc);
- let dref: Opt<DocumentView>;
- const stackedDocTransform = () => this.getDocTransform(doc, dref);
+ const stackedDocTransform = () => this.getDocTransform(doc);
this._docXfs.push({ stackedDocTransform, width, height });
- //DocumentView is how the node will be rendered
return count > this._renderCount ? null : (
<DocumentView
- ref={r => (dref = r || undefined)}
+ ref={action((r: DocumentView) => r?.ContentDiv && this.docRefs.set(doc, r))}
Document={doc}
DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
renderDepth={this.props.renderDepth + 1}
@@ -368,9 +366,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
);
}
- getDocTransform(doc: Doc, dref?: DocumentView) {
- const y = this._scroll; // required for document decorations to update when the text box container is scrolled
- const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
+ getDocTransform(doc: Doc) {
+ const dref = this.docRefs.get(doc);
+ this._scroll; // must be referenced for document decorations to update when the text box container is scrolled
+ const { translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv);
// the document view may center its contents and if so, will prepend that onto the screenToLocalTansform. so we have to subtract that off
return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
}
@@ -409,7 +408,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
e,
this.onDividerMove,
action(() => {
- this._cursor = 'grab';
+ this._cursor = 'ew-resize';
batch.end();
}),
emptyFunction
@@ -423,7 +422,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
@computed get columnDragger() {
return (
- <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
+ <div
+ className="collectionStackingView-columnDragger"
+ onPointerDown={this.columnDividerDown}
+ ref={this._draggerRef}
+ style={{ cursor: this._cursor, color: StrCast(Doc.UserDoc().userColor), left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
<FontAwesomeIcon icon={'arrows-alt-h'} />
</div>
);
@@ -595,7 +598,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
action((entries: any) => {
if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
- this.props.setHeight?.(this.headerMargin + height);
+ this.props.setHeight?.(2 * this.headerMargin + height); // bcz: added 2x for header to fix problem with scrollbars appearing in Tools panel
}
})
);
@@ -666,7 +669,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
if (menuDoc) {
const width: number = NumCast(menuDoc._width, 30);
const height: number = NumCast(menuDoc._height, 30);
- console.log(menuDoc.title, width, height);
return (
<div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
<DocumentView
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 00137736d..ea3b5065f 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -150,6 +150,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
console.log('WHAAAT');
}
dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction;
+ e.stopPropagation();
}
};
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 1ff5688f9..71032ff68 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -400,7 +400,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return NumCast(Cast(PresBox.Instance.activeItem.presentationTargetDoc, Doc, null)._currentFrame);
};
static Activate = (tabDoc: Doc) => {
- const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc);
+ const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc && !tab.contentItem.config.props.keyValue);
tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
return tab !== undefined;
};
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index d904749b1..25a547066 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from '../../util/DocumentManager';
@@ -127,7 +127,7 @@ export class TreeView extends React.Component<TreeViewProps> {
: this.props.treeView.fileSysMode
? this.doc.isFolder
? this.fieldKey
- : 'embeddings' // for displaying
+ : 'data' // file system folders display their contents (data). used to be they displayed their embeddings but now its a tree structure and not a flat list
: this.props.treeView.outlineMode || this.childDocs
? this.fieldKey
: Doc.noviceMode
@@ -222,10 +222,8 @@ export class TreeView extends React.Component<TreeViewProps> {
this.treeViewOpen = !this.treeViewOpen;
} else {
// choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns, (2) has no context field ... otherwise make a new embedding
- const bestEmbedding = docView.rootDoc.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document)
- ? docView.rootDoc
- : Doc.BestEmbedding(docView.rootDoc);
- this.props.addDocTab(bestEmbedding, OpenWhere.lightbox);
+ const bestEmbedding = docView.rootDoc.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) ? docView.rootDoc : Doc.BestEmbedding(docView.rootDoc);
+ this.props.addDocTab(bestEmbedding, OpenWhere.lightbox);
}
};
@@ -344,7 +342,6 @@ export class TreeView extends React.Component<TreeViewProps> {
TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
return this.props.addDocument(folder);
};
- deleteItem = () => this.props.removeDoc?.(this.doc);
preTreeDrop = (e: Event, de: DragManager.DropEvent) => {
const dragData = de.complete.docDragData;
@@ -540,8 +537,6 @@ export class TreeView extends React.Component<TreeViewProps> {
TraceMobx();
const expandKey = this.treeViewExpandedView;
const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; icon: JSX.Element | string } }) ?? {};
- const color = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Color)
- console.log("tree view", color, this.doc.title, Doc.IsSystem(this.doc))
if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) {
const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
const sortKeys = Object.keys(sortings);
@@ -581,9 +576,7 @@ export class TreeView extends React.Component<TreeViewProps> {
);
}
return (
- <div style={{
- color: color
- }}>
+ <div>
{!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : (
<div className={'treeView-sorting'}>
<IconButton
@@ -662,14 +655,14 @@ export class TreeView extends React.Component<TreeViewProps> {
);
} else if (this.treeViewExpandedView === 'fields') {
return (
- <ul key={this.doc[Id] + this.doc.title} style={{ cursor: 'inherit', color: color }}>
+ <ul key={this.doc[Id] + this.doc.title} style={{ cursor: 'inherit' }}>
<div>{this.expandedField}</div>
</ul>
);
}
return (
<ul
- style={{ color: color }}
+ style={{}}
onPointerDown={e => {
e.preventDefault();
e.stopPropagation();
@@ -725,26 +718,19 @@ export class TreeView extends React.Component<TreeViewProps> {
}>
{this.props.treeView.outlineMode ? (
!(this.doc.text as RichTextField)?.Text ? null : (
- <IconButton
- color={color}
- icon={<FontAwesomeIcon
- icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />}
- size={Size.XSMALL}
- />
+ <IconButton color={color} icon={<FontAwesomeIcon icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />} size={Size.XSMALL} />
)
) : (
<div className="treeView-bulletIcons" style={{ color: Doc.IsSystem(DocCast(this.doc.proto)) ? 'red' : undefined }}>
- {this.onCheckedClick ? <IconButton
- color={color}
- icon={<FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />}
- size={Size.XSMALL}
- /> :
+ {this.onCheckedClick ? (
<IconButton
- color={color}
- icon={<FontAwesomeIcon icon={iconType as IconProp} />}
+ color={color}
+ icon={<FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />}
size={Size.XSMALL}
/>
- }
+ ) : (
+ <IconButton color={color} icon={<FontAwesomeIcon icon={iconType as IconProp} />} size={Size.XSMALL} />
+ )}
</div>
)}
</div>
@@ -776,17 +762,15 @@ export class TreeView extends React.Component<TreeViewProps> {
return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : (
<>
{customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */}
- {this.doc._layout_hideContextMenu ? null : (
- <IconButton
- color={color}
- icon={<FontAwesomeIcon icon="bars" />}
- size={Size.XSMALL}
- onClick={e => {
- this.showContextMenu(e);
- e.stopPropagation();
- }}
- />
- )}
+ <IconButton
+ color={color}
+ icon={<FontAwesomeIcon icon="bars" />}
+ size={Size.XSMALL}
+ onClick={e => {
+ this.showContextMenu(e);
+ e.stopPropagation();
+ }}
+ />
{Doc.noviceMode ? null : this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
<span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
{this.treeViewExpandedView}
@@ -803,10 +787,10 @@ export class TreeView extends React.Component<TreeViewProps> {
};
contextMenuItems = () => {
const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' };
- const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' };
const folderOp = this.childDocs?.length ? [makeFolder] : [];
const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(self), "${OpenWhere.addRight}")`)!, icon: 'copy', label: 'Open New Embedding' };
const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' };
+ const reopenDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Reopen' };
return [
...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
...(this.doc.isFolder
@@ -817,7 +801,9 @@ export class TreeView extends React.Component<TreeViewProps> {
? [openEmbedding, makeFolder]
: this.doc._type_collection === CollectionViewType.Docking
? []
- : [deleteItem, openEmbedding, focusDoc]),
+ : this.props.treeView.rootDoc === Doc.MyRecentlyClosed
+ ? [reopenDoc]
+ : [openEmbedding, focusDoc]),
];
};
childContextMenuItems = () => {
@@ -917,6 +903,7 @@ export class TreeView extends React.Component<TreeViewProps> {
height={12}
sizeToContent={true}
fontSize={12}
+ isEditingCallback={action(e => (this._editTitle = e))}
GetValue={() => StrCast(this.doc.title)}
OnTab={undoBatch((shift?: boolean) => {
if (!shift) this.props.indentDocument?.(true);
@@ -989,6 +976,8 @@ export class TreeView extends React.Component<TreeViewProps> {
ref={this._tref}
title="click to edit title. Double Click or Drag to Open"
style={{
+ backgroundColor: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? StrCast(Doc.UserDoc().userVariantColor) : undefined,
+ color: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? lightOrDark(StrCast(Doc.UserDoc().userVariantColor)) : undefined,
fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined,
textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined,
outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined,
@@ -1014,12 +1003,13 @@ export class TreeView extends React.Component<TreeViewProps> {
onPointerDown={this.ignoreEvent}
onPointerEnter={this.onPointerEnter}
onPointerLeave={this.onPointerLeave}>
- {contents}
- <div className={`treeView-background`}
+ <div
+ className="treeView-background"
style={{
- background: StrCast(Doc.UserDoc().userColor)
+ background: StrCast(Doc.UserDoc().userColor),
}}
/>
+ {contents}
</div>
{this.renderBorder}
</>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 34f707b30..29122cb91 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1264,13 +1264,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.addDocument?.(newDoc);
}
};
- pointerEvents = () => {
+ @computed get _pointerEvents() {
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
const pointerEvents = DocumentDecorations.Instance.Interacting
? 'none'
: this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
return pointerEvents;
- };
+ }
+ pointerEvents = () => this._pointerEvents;
+ childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
@@ -1299,7 +1301,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
isDocumentActive={this.props.childDocumentsActive?.() || this.rootDoc._isGroup ? this.props.isDocumentActive : this.isContentActive}
- isContentActive={this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction}
+ isContentActive={this.childContentsActive}
focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
addDocTab={this.addDocTab}
addDocument={this.props.addDocument}
@@ -1316,7 +1318,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
layout_showTitle={this.props.childlayout_showTitle}
dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
pointerEvents={this.pointerEvents}
- //rotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
//fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeViewFreezeChildDimensions)} // bcz: check this
/>
);
@@ -1356,6 +1357,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _lightboxDoc: Opt<Doc>;
getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
+ const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
const childDoc = params.pair.layout;
const childDocLayout = Doc.Layout(childDoc);
const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values
@@ -1366,11 +1368,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
layoutFrameNumber === undefined
? { _width: Cast(childDocLayout._width, 'number'), _height: Cast(childDocLayout._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() }
: CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
+ // prettier-ignore
+ const rotation = Cast(_rotation,'number',
+ !this.layoutDoc._rotation_jitter ? null
+ : NumCast(this.layoutDoc._rotation_jitter) * random(-1, 1, NumCast(x), NumCast(y)) );
return {
x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
z: Cast(z, 'number'),
- rotation: Cast(_rotation, 'number'),
+ rotation: rotation,
color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.getClusterColor(childDoc, this.props, StyleProp.BackgroundColor),
opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
@@ -1435,21 +1441,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}.bind(this)
);
- childPositionProviderUnmemoized = (doc: Doc, replica: string) => {
- return this._layoutPoolData.get(doc[Id] + (replica || ''));
- };
+ childPositionProviderUnmemoized = (doc: Doc, replica: string) => this._layoutPoolData.get(doc[Id] + (replica || ''));
childDataProvider = computedFn(
function childDataProvider(this: any, doc: Doc, replica: string) {
- return this._layoutPoolData.get(doc[Id] + (replica || ''));
+ return this.childPositionProviderUnmemoized(doc, replica);
}.bind(this)
);
- childSizeProviderUnmemoized = (doc: Doc, replica: string) => {
- return this._layoutSizeData.get(doc[Id] + (replica || ''));
- };
+ childSizeProviderUnmemoized = (doc: Doc, replica: string) => this._layoutSizeData.get(doc[Id] + (replica || ''));
childSizeProvider = computedFn(
function childSizeProvider(this: any, doc: Doc, replica: string) {
- return this._layoutSizeData.get(doc[Id] + (replica || ''));
+ return this.childSizeProviderUnmemoized(doc, replica);
}.bind(this)
);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 090cf356c..7c53bfdbe 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -377,7 +377,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
: ((doc: Doc) => {
Doc.GetProto(doc).data = new List<Doc>(selected);
Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
- !this.props.isAnnotationOverlay && Doc.AddFileOrphan(Doc.GetProto(doc));
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 56b8366d0..2254b2e5f 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -204,7 +204,7 @@ export class CollectionLinearView extends CollectionSubView() {
const menuOpener = (
<Toggle
- text={Cast(this.props.Document.icon, 'string', null)}
+ text={Cast(this.props.Document.icon, 'string', null)}
icon={Cast(this.props.Document.icon, 'string', null) ? undefined : <FontAwesomeIcon icon={isExpanded ? 'minus' : 'plus'} />}
type={Type.TERT}
color={StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}
@@ -212,7 +212,7 @@ export class CollectionLinearView extends CollectionSubView() {
toggleType={ToggleType.BUTTON}
toggleStatus={BoolCast(this.layoutDoc.linearView_IsOpen)}
onClick={() => {
- this.layoutDoc.linearView_IsOpen = !isExpanded;
+ this.layoutDoc.linearView_IsOpen = !isExpanded;
}}
tooltip={isExpanded ? 'Close' : 'Open'}
fillWidth={true}
@@ -223,25 +223,22 @@ export class CollectionLinearView extends CollectionSubView() {
return (
<div className={`collectionLinearView-outer ${this.layoutDoc.linearView_SubMenu}`} style={{ backgroundColor: this.layoutDoc.linearView_IsOpen ? undefined : 'transparent' }}>
<div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension(), pointerEvents: 'all' }}>
- {this.props.Document.linearView_Dropdown ?
- <div>Hello World!</div>
- :
+ {
<>
- {!this.props.Document.linearView_Expandable ? null : menuOpener}
- {!this.layoutDoc.linearView_IsOpen ? null : (
- <div
- className="collectionLinearView-content"
- style={{
- height: this.dimension(),
- flexDirection: flexDir as any,
- gap: flexGap,
- }}>
- {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
- </div>
- )}
+ {menuOpener}
+ {!this.layoutDoc.linearView_IsOpen ? null : (
+ <div
+ className="collectionLinearView-content"
+ style={{
+ height: this.dimension(),
+ flexDirection: flexDir as any,
+ gap: flexGap,
+ }}>
+ {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
+ </div>
+ )}
</>
}
-
</div>
</div>
);
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index ee5bf82ed..babe5c810 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -810,7 +810,6 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get sortedDocs() {
- trace();
const field = StrCast(this.layoutDoc.sortField);
const desc = BoolCast(this.layoutDoc.sortDesc);
const docs = !field
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 2ea8a7846..256377758 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -10,7 +10,7 @@ import { GestureUtils } from '../../../pen-gestures/GestureUtils';
import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
-import { UndoManager } from '../../util/UndoManager';
+import { undoable, UndoManager } from '../../util/UndoManager';
import { GestureOverlay } from '../GestureOverlay';
import { InkTranscription } from '../InkTranscription';
import { ActiveFillColor, SetActiveFillColor, ActiveIsInkMask, SetActiveIsInkMask, ActiveInkWidth, SetActiveInkWidth, ActiveInkColor, SetActiveInkColor } from '../InkingStroke';
@@ -33,7 +33,6 @@ ScriptingGlobals.add(function setView(view: string) {
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
const selectedViews = SelectionManager.Views();
- console.log(color, checkResult);
if (Doc.ActiveTool !== InkTool.None) {
if (checkResult) {
return ActiveFillColor();
@@ -54,7 +53,6 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
if (contentFrameNumber !== undefined) {
CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color });
} else {
- console.log('setting color to: ', color);
dv.rootDoc['_' + fieldKey] = color;
}
});
@@ -79,7 +77,6 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
- console.log(checkResult);
const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
if (checkResult) {
if (NumCast(selected?.Document.z) >= 1) return true;
@@ -121,7 +118,6 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snapli
]);
if (checkResult) {
- console.log(attr, map.get(attr)?.checkResult(selected));
return map.get(attr)?.checkResult(selected);
}
const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
@@ -195,14 +191,12 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
const map = new Map(attrs.concat(alignments).concat(listings));
if (checkResult) {
- console.log(charStyle, checkResult, map.get(charStyle)?.checkResult());
return map.get(charStyle)?.checkResult();
}
- map.get(charStyle)?.toggle();
+ undoable(() => map.get(charStyle)?.toggle(), 'toggle ' + charStyle)();
});
export function checkInksToGroup() {
- // console.log("getting here to inks group");
if (Doc.ActiveTool === InkTool.Write) {
CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
// TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
index 0ede75407..ff17e5c12 100644
--- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
+++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
@@ -1,6 +1,6 @@
import './ButtonMenu.scss';
import * as React from 'react';
-import { IButtonMenu } from "./utils";
+import { IButtonMenu } from './utils';
import { NewLightboxView } from '../NewLightboxView';
import { SelectionManager } from '../../../util/SelectionManager';
import { CollectionDockingView } from '../../collections/CollectionDockingView';
@@ -11,43 +11,40 @@ import { MainView } from '../../MainView';
import { action } from 'mobx';
export const ButtonMenu = (props: IButtonMenu) => {
-
- return <div className={`newLightboxButtonMenu-container`}>
- <div
- className="newLightboxView-navBtn"
- title="toggle fit width"
- onClick={e => {
- e.stopPropagation();
- NewLightboxView.NewLightboxDoc!._fitWidth = !NewLightboxView.NewLightboxDoc!._fitWidth;
- }}>
- </div>
- <div
- className="newLightboxView-tabBtn"
- title="open in tab"
- onClick={e => {
- e.stopPropagation();
- CollectionDockingView.AddSplit(NewLightboxView.NewLightboxDoc || NewLightboxView.NewLightboxDoc!, OpenWhereMod.none);
- SelectionManager.DeselectAll();
- NewLightboxView.SetNewLightboxDoc(undefined);
- }}>
- </div>
- <div
- className="newLightboxView-penBtn"
- title="toggle pen annotation"
- style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }}
- onClick={e => {
- e.stopPropagation();
- Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
- }}>
- </div>
- <div
- className="newLightboxView-exploreBtn"
- title="toggle explore mode to navigate among documents only"
- style={{ background: MainView.Instance._exploreMode ? 'white' : undefined }}
- onClick={action(e => {
- e.stopPropagation();
- MainView.Instance._exploreMode = !MainView.Instance._exploreMode;
- })}>
- </div>
- </div>
-} \ No newline at end of file
+ return (
+ <div className={`newLightboxButtonMenu-container`}>
+ <div
+ className="newLightboxView-navBtn"
+ title="toggle fit width"
+ onClick={e => {
+ e.stopPropagation();
+ NewLightboxView.LightboxDoc!._fitWidth = !NewLightboxView.LightboxDoc!._fitWidth;
+ }}></div>
+ <div
+ className="newLightboxView-tabBtn"
+ title="open in tab"
+ onClick={e => {
+ e.stopPropagation();
+ CollectionDockingView.AddSplit(NewLightboxView.LightboxDoc || NewLightboxView.LightboxDoc!, OpenWhereMod.none);
+ SelectionManager.DeselectAll();
+ NewLightboxView.SetNewLightboxDoc(undefined);
+ }}></div>
+ <div
+ className="newLightboxView-penBtn"
+ title="toggle pen annotation"
+ style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }}
+ onClick={e => {
+ e.stopPropagation();
+ Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
+ }}></div>
+ <div
+ className="newLightboxView-exploreBtn"
+ title="toggle explore mode to navigate among documents only"
+ style={{ background: MainView.Instance._exploreMode ? 'white' : undefined }}
+ onClick={action(e => {
+ e.stopPropagation();
+ MainView.Instance._exploreMode = !MainView.Instance._exploreMode;
+ })}></div>
+ </div>
+ );
+};
diff --git a/src/client/views/newlightbox/ExploreView/ExploreView.tsx b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
index 855bfd9e2..a1d6375c4 100644
--- a/src/client/views/newlightbox/ExploreView/ExploreView.tsx
+++ b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
@@ -1,30 +1,32 @@
import './ExploreView.scss';
-import { IBounds, IExploreView, emptyBounds } from "./utils";
-import { IRecommendation } from "../components";
+import { IBounds, IExploreView, emptyBounds } from './utils';
+import { IRecommendation } from '../components';
import * as React from 'react';
import { NewLightboxView } from '../NewLightboxView';
import { StrCast } from '../../../../fields/Types';
-
-
export const ExploreView = (props: IExploreView) => {
- const { recs, bounds=emptyBounds } = props
-
- return <div className={`exploreView-container`}>
- {recs && recs.map((rec) => {
- console.log(rec.embedding, bounds)
- const x_bound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x))
- const y_bound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y))
- console.log(x_bound, y_bound)
- if (rec.embedding) {
- const x = (rec.embedding.x / x_bound) * 50;
- const y = (rec.embedding.y / y_bound) * 50;
- console.log(x, y)
- return <div className={`exploreView-doc`} onClick={() => {}} style={{top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)`}}>
- {rec.title}
- </div>
- } else return (null)
- })}
- <div className={`exploreView-doc`} style={{top: `calc(50% + ${0}%)`, left: `calc(50% + ${0}%)`, background: '#073763', color: 'white'}}>{StrCast(NewLightboxView.NewLightboxDoc?.title)}</div>
- </div>
-} \ No newline at end of file
+ const { recs, bounds = emptyBounds } = props;
+
+ return (
+ <div className={`exploreView-container`}>
+ {recs &&
+ recs.map(rec => {
+ const x_bound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x));
+ const y_bound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y));
+ if (rec.embedding) {
+ const x = (rec.embedding.x / x_bound) * 50;
+ const y = (rec.embedding.y / y_bound) * 50;
+ return (
+ <div className={`exploreView-doc`} onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}>
+ {rec.title}
+ </div>
+ );
+ } else return null;
+ })}
+ <div className={`exploreView-doc`} style={{ top: `calc(50% + ${0}%)`, left: `calc(50% + ${0}%)`, background: '#073763', color: 'white' }}>
+ {StrCast(NewLightboxView.LightboxDoc?.title)}
+ </div>
+ </div>
+ );
+};
diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
index c0d357ad5..2c2f04b9f 100644
--- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
+++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { IRecommendation } from "./utils";
+import { IRecommendation } from './utils';
import './Recommendation.scss';
import { getType } from '../../utils';
import { FaEyeSlash } from 'react-icons/fa';
@@ -9,82 +9,94 @@ import { Doc } from '../../../../../fields/Doc';
import { Docs } from '../../../../documents/Documents';
export const Recommendation = (props: IRecommendation) => {
- const {title, data, type, text, transcript, loading, source, previewUrl, related_concepts, distance, docId} = props
-
- return <div className={`recommendation-container ${loading && 'loading'} ${previewUrl && 'previewUrl'}`} onClick={() => {
- let doc: Doc | null = null;
- if (source == "Dash" && docId) {
- const docView = DocumentManager.Instance.getDocumentViewById(docId)
- if (docView) {
- doc = docView.rootDoc;
- }
- } else if (data) {
- console.log(data, type)
- switch(type) {
- case "YouTube":
- console.log('create ', type, 'document')
- doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript: transcript })
- break;
- case "Video":
- console.log('create ', type, 'document')
- doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript: transcript })
- break;
- case "Webpage":
- console.log('create ', type, 'document')
- doc = Docs.Create.WebDocument(data, { title: title, text: text })
- break;
- case "HTML":
- console.log('create ', type, 'document')
- doc = Docs.Create.WebDocument(data, { title: title, text: text })
- break;
- case "Text":
- console.log('create ', type, 'document')
- doc = Docs.Create.TextDocument(data, { title: title, text: text })
- break;
- case "PDF":
- console.log('create ', type, 'document')
- doc = Docs.Create.PdfDocument(data, { title: title, text: text })
- break;
- }
- }
- if (doc !== null) NewLightboxView.SetNewLightboxDoc(doc)
- }}>
- {loading ?
- <div className={`image-container`}>
- </div>
- :
- previewUrl ? <div className={`image-container`}>
- {<img className={`image`} src={previewUrl}></img>}
- </div>
- : null
- }
- <div className={`title`}>{title}</div>
- <div className={`info`}>
- {!loading && <div className={`type-container`}>
- <div className={`lb-label`}>Type</div><div className={`lb-type`}>{getType(type!)}</div>
- </div>}
- {!loading && <div className={`distance-container`}>
- <div className={`lb-label`}>Distance</div><div className={`lb-distance`}>{distance}</div>
- </div>}
- </div>
- <div className={`source`}>
- {!loading && <div className={`source-container`}>
- <div className={`lb-label`}>Source</div><div className={`lb-source`}>{source}</div>
- </div>}
- </div>
- <div className={`explainer`}>
- {!loading &&
- <div>
- You are seeing this recommendation because this document also explores
- <div className={`concepts-container`}>
- {related_concepts?.map((val) => {
- return <div className={'concept'}>{val}</div>
- })}
- </div>
- </div>}
- </div>
- <div className={`hide-rec`}>
- {!loading && <><div>Hide Recommendation</div><div style={{fontSize: 15, paddingRight: 5}}><FaEyeSlash/></div></>}
+ const { title, data, type, text, transcript, loading, source, previewUrl, related_concepts, distance, docId } = props;
+
+ return (
+ <div
+ className={`recommendation-container ${loading && 'loading'} ${previewUrl && 'previewUrl'}`}
+ onClick={() => {
+ let doc: Doc | null = null;
+ if (source == 'Dash' && docId) {
+ const docView = DocumentManager.Instance.getDocumentViewsById(docId).lastElement();
+ if (docView) {
+ doc = docView.rootDoc;
+ }
+ } else if (data) {
+ switch (type) {
+ case 'YouTube':
+ console.log('create ', type, 'document');
+ doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript: transcript });
+ break;
+ case 'Video':
+ console.log('create ', type, 'document');
+ doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript: transcript });
+ break;
+ case 'Webpage':
+ console.log('create ', type, 'document');
+ doc = Docs.Create.WebDocument(data, { title: title, text: text });
+ break;
+ case 'HTML':
+ console.log('create ', type, 'document');
+ doc = Docs.Create.WebDocument(data, { title: title, text: text });
+ break;
+ case 'Text':
+ console.log('create ', type, 'document');
+ doc = Docs.Create.TextDocument(data, { title: title, text: text });
+ break;
+ case 'PDF':
+ console.log('create ', type, 'document');
+ doc = Docs.Create.PdfDocument(data, { title: title, text: text });
+ break;
+ }
+ }
+ if (doc !== null) NewLightboxView.SetNewLightboxDoc(doc);
+ }}>
+ {loading ? <div className={`image-container`}></div> : previewUrl ? <div className={`image-container`}>{<img className={`image`} src={previewUrl}></img>}</div> : null}
+ <div className={`title`}>{title}</div>
+ <div className={`info`}>
+ {!loading && (
+ <div className={`type-container`}>
+ <div className={`lb-label`}>Type</div>
+ <div className={`lb-type`}>{getType(type!)}</div>
+ </div>
+ )}
+ {!loading && (
+ <div className={`distance-container`}>
+ <div className={`lb-label`}>Distance</div>
+ <div className={`lb-distance`}>{distance}</div>
+ </div>
+ )}
+ </div>
+ <div className={`source`}>
+ {!loading && (
+ <div className={`source-container`}>
+ <div className={`lb-label`}>Source</div>
+ <div className={`lb-source`}>{source}</div>
+ </div>
+ )}
+ </div>
+ <div className={`explainer`}>
+ {!loading && (
+ <div>
+ You are seeing this recommendation because this document also explores
+ <div className={`concepts-container`}>
+ {related_concepts?.map(val => {
+ return <div className={'concept'}>{val}</div>;
+ })}
+ </div>
+ </div>
+ )}
+ </div>
+ <div className={`hide-rec`}>
+ {!loading && (
+ <>
+ <div>Hide Recommendation</div>
+ <div style={{ fontSize: 15, paddingRight: 5 }}>
+ <FaEyeSlash />
+ </div>
+ </>
+ )}
+ </div>
</div>
- </div>
-} \ No newline at end of file
+ );
+};
diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
index e1ff6f8eb..10bfb0c64 100644
--- a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
+++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
@@ -34,7 +34,6 @@ export const createLineGenerator = (xScale: d3.ScaleLinear<number, number, never
};
export const xAxisCreator = (g: d3.Selection<SVGGElement, unknown, null, undefined>, height: number, xScale: d3.ScaleLinear<number, number, never>) => {
- console.log('x axis creator being called');
g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15));
};
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 7e8eef0a5..2d8663c9c 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -18,7 +18,6 @@ import { SearchBox } from '../search/SearchBox';
import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo';
import { YoutubeBox } from './../../apis/youtube/YoutubeBox';
import { AudioBox } from './AudioBox';
-import { FontIconBox } from './FontIconBox/FontIconBox';
import { ColorBox } from './ColorBox';
import { ComparisonBox } from './ComparisonBox';
import { DataVizBox } from './DataVizBox/DataVizBox';
@@ -26,9 +25,11 @@ import { DocumentViewProps } from './DocumentView';
import './DocumentView.scss';
import { EquationBox } from './EquationBox';
import { FieldView, FieldViewProps } from './FieldView';
+import { FontIconBox } from './FontIconBox/FontIconBox';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { FunctionPlotBox } from './FunctionPlotBox';
import { ImageBox } from './ImageBox';
+import { ImportElementBox } from './importBox/ImportElementBox';
import { KeyValueBox } from './KeyValueBox';
import { LabelBox } from './LabelBox';
import { LinkAnchorBox } from './LinkAnchorBox';
@@ -269,6 +270,7 @@ export class DocumentContentsView extends React.Component<
LoadingBox,
PhysicsSimulationBox,
SchemaRowBox,
+ ImportElementBox,
}}
bindings={bindings}
jsx={layoutFrame}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 66352678c..bb9f45bdd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -47,11 +47,11 @@ import { DocumentLinksButton } from './DocumentLinksButton';
import './DocumentView.scss';
import { FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
-import { KeyValueBox } from './KeyValueBox';
import { LinkAnchorBox } from './LinkAnchorBox';
import { PresEffect, PresEffectDirection } from './trails';
import { PinProps, PresBox } from './trails/PresBox';
import React = require('react');
+import { SettingsManager } from '../../util/SettingsManager';
const { Howl } = require('howler');
interface Window {
@@ -314,7 +314,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get titleHeight() {
return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
}
- get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
+ @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
}
@computed get finalLayoutKey() {
@@ -332,8 +332,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
return (
DocumentView.LongPress ||
onScriptDisable === 'always' ||
- (onScriptDisable !== 'never' && (this.rootSelected() || this.props.isSelected())) ||
- this._componentView?.isAnyChildContentActive?.()
+ (onScriptDisable !== 'never' && (this.rootSelected() || this._componentView?.isAnyChildContentActive?.()))
);
}
@computed get onClickHandler() {
@@ -800,7 +799,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
const constantItems: ContextMenuProps[] = [];
if (!Doc.IsSystem(this.rootDoc)) {
- constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
+ constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
(this.rootDoc._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) {
// need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
@@ -863,34 +862,32 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
};
- rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
+ @computed get _rootSelected() {
+ return this.props.isSelected(false) || (this.props.Document.rootDocument && this.props.rootSelected?.(false)) || false;
+ }
+ rootSelected = (outsideReaction?: boolean) => this._rootSelected;
panelHeight = () => this.props.PanelHeight() - this.headerMargin;
screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
setHeight = (height: number) => (this.layoutDoc._height = height);
setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
- isContentActive = (outsideReaction?: boolean): boolean | undefined => {
+ @computed get _isContentActive() {
// true - if the document has been activated directly or indirectly (by having its children selected)
// false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive
// undefined - it is not active, but it should be responsive to actions that might active it or its contents (eg clicking)
- return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none' || (this.rootDoc.pointerEvents === 'none' && !StrCast(this.props.LayoutTemplateString).includes(KeyValueBox.name))
+ return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none'
? false
- : Doc.ActiveTool !== InkTool.None ||
- SnappingManager.GetIsDragging() ||
- this.rootSelected() ||
- this.rootDoc.forceActive ||
- this.props.isSelected(outsideReaction) ||
- this._componentView?.isAnyChildContentActive?.() ||
- this.props.isContentActive()
+ : Doc.ActiveTool !== InkTool.None || SnappingManager.GetIsDragging() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()
? true
: undefined;
- };
+ }
+ isContentActive = (): boolean | undefined => this._isContentActive;
@observable _retryThumb = 1;
- thumbShown = () => {
- const childHighlighted = () =>
- Array.from(Doc.highlightedDocs.keys())
- .concat(Array.from(Doc.brushManager.BrushedDoc.keys()))
- .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
+ @computed get _thumbShown() {
+ const childHighlighted = () => false;
+ // Array.from(Doc.highlightedDocs.keys())
+ // .concat(Array.from(Doc.brushManager.BrushedDoc.keys()))
+ // .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc));
return !this.props.LayoutTemplateString &&
!this.isContentActive() &&
@@ -900,12 +897,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking)
? true
: false;
- };
+ }
+ thumbShown = () => this._thumbShown;
childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
- /// disable pointer events on content when there's an enabled onClick script (but not the browse script), or if contents are marked inactive
- contentPointerEvents = () => ((!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.()) || this.isContentActive() === false ? 'none' : this.pointerEvents);
-
+ /// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive
+ @computed get _contentPointerEvents() {
+ return (!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.() && this.isContentActive() !== true) || this.isContentActive() === false ? 'none' : this.pointerEvents;
+ }
+ contentPointerEvents = () => this._contentPointerEvents;
@computed get contents() {
TraceMobx();
const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString;
@@ -1088,7 +1088,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
<div
className="documentView-captionWrapper"
style={{
- pointerEvents: this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+ pointerEvents: this.rootDoc.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
minWidth: 50 * ffscale(),
maxHeight: `max(100%, ${20 * ffscale()}px)`,
}}>
@@ -1110,7 +1110,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
const background = StrCast(
SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor,
- Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().headingColor) : 'rgba(0,0,0,0.4)'
+ Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().headingColor) : SettingsManager.Instance.userVariantColor
);
const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
const titleView = !showTitle ? null : (
@@ -1223,16 +1223,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
case PresEffect.Lightspeed: return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
}
}
+ @computed get highlighting() {
+ return this.props.styleProvider?.(this.props.Document, this.props, StyleProp.Highlighting);
+ }
+ @computed get borderPath() {
+ return this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath);
+ }
render() {
TraceMobx();
- const highlighting = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.Highlighting);
- const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath);
+ const highlighting = this.highlighting;
+ const borderPath = this.borderPath;
const boxShadow =
this.props.treeViewDoc || !highlighting
? this.boxShadow
: highlighting && this.borderRounding && highlighting.highlightStyle !== 'dashed'
? `0 0 0 ${highlighting.highlightIndex}px ${highlighting.highlightColor}`
- : this.boxShadow || (this.props.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
+ : this.boxShadow || (this.rootDoc.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
const renderDoc = this.renderDoc({
borderRadius: this.borderRounding,
outline: highlighting && !this.borderRounding && !highlighting.highlightStroke ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px',
@@ -1248,9 +1254,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onContextMenu={this.onContextMenu}
onPointerDown={this.onPointerDown}
onClick={this.onClick}
- onPointerEnter={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.props.Document)}
- onPointerOver={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.props.Document)}
- onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.props.Document)}
+ onPointerEnter={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)}
+ onPointerOver={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)}
+ onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.rootDoc)}
style={{
borderRadius: this.borderRounding,
pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents,
diff --git a/src/client/views/nodes/EquationBox.scss b/src/client/views/nodes/EquationBox.scss
index 9714e1bd0..f5871db22 100644
--- a/src/client/views/nodes/EquationBox.scss
+++ b/src/client/views/nodes/EquationBox.scss
@@ -1,8 +1,9 @@
-@import "../global/globalCssVariables.scss";
+@import '../global/globalCssVariables.scss';
.equationBox-cont {
- transform-origin: top left;
+ transform-origin: center;
+ background-color: #e7e7e7;
> span {
- width: 100%;
+ width: 100%;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 3ec53beac..91eac675f 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -1,6 +1,6 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
+import { Button, MultiToggle, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -21,6 +21,7 @@ import { RichTextMenu } from '../formattedText/RichTextMenu';
import './FontIconBox.scss';
import { SelectedDocView } from '../../selectedDoc';
import { Utils } from '../../../../Utils';
+import { FaAlignCenter, FaAlignJustify, FaAlignLeft, FaAlignRight } from 'react-icons/fa';
export enum ButtonType {
TextButton = 'textBtn',
@@ -86,10 +87,10 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
Icon = (color: string, iconFalse?: boolean) => {
let icon;
- if (iconFalse ) {
+ if (iconFalse) {
icon = StrCast(this.dataDoc[this.fieldKey ?? 'iconFalse'] ?? this.dataDoc.icon, 'user') as any;
- if (icon) return <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />
- else return null
+ if (icon) return <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
+ else return null;
}
icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any;
const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />;
@@ -124,36 +125,38 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
*/
@computed get numberDropdown() {
let type: NumberDropdownType;
- switch(this.type) {
- case ButtonType.NumberDropdownButton:
- type = 'dropdown'
+ switch (this.type) {
+ case ButtonType.NumberDropdownButton:
+ type = 'dropdown';
break;
case ButtonType.NumberInlineButton:
- type = 'input'
+ type = 'input';
break;
case ButtonType.NumberSliderButton:
default:
- type = 'slider'
+ type = 'slider';
break;
}
- const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ self: this.rootDoc, value, _readOnly_: value === undefined });
+ const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ this: this.layoutDoc, self: this.rootDoc, value, _readOnly_: value === undefined });
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
// Script for checking the outcome of the toggle
const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));
const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
- return <NumberDropdown
- color={color}
- numberDropdownType={type}
- showPlusMinus={false}
- tooltip={this.label}
- type={Type.PRIM}
- min={NumCast(this.rootDoc.numBtnMin, 0)}
- max={NumCast(this.rootDoc.numBtnMax, 100)}
- number={checkResult}
- setNumber={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}
- fillWidth
- />
+ return (
+ <NumberDropdown
+ color={color}
+ numberDropdownType={type}
+ showPlusMinus={false}
+ tooltip={this.label}
+ type={Type.PRIM}
+ min={NumCast(this.rootDoc.numBtnMin, 0)}
+ max={NumCast(this.rootDoc.numBtnMax, 100)}
+ number={checkResult}
+ setNumber={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}
+ fillWidth
+ />
+ );
}
/**
@@ -202,84 +205,64 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
let dropdown = true;
let getStyle: (val: string) => any = () => {};
let icon: IconProp = 'caret-down';
- let isViewDropdown: boolean = script?.script.originalScript.startsWith('setView')
+ let isViewDropdown: boolean = script?.script.originalScript.startsWith('setView');
try {
if (isViewDropdown) {
const selectedDocs: Doc[] = SelectionManager.Docs();
const selected = SelectionManager.Docs().lastElement();
- console.log('selected')
if (selected) {
if (StrCast(selected.type) === DocumentType.COL) {
text = StrCast(selected._type_collection);
- console.log("collection selected", text)
} else {
- console.log("doc selected", selected.title);
dropdown = false;
if (selectedDocs.length > 1) {
- text = selectedDocs.length + " selected"
+ text = selectedDocs.length + ' selected';
} else {
text = Utils.cleanDocumentType(StrCast(selected.type) as DocumentType);
icon = Doc.toIcon(selected);
}
- return <Popup
- icon={<FontAwesomeIcon size={'1x'} icon={icon} />}
- text={text}
- type={Type.TERT}
- color={color}
- popup={<SelectedDocView selectedDocs={selectedDocs}/>}
- fillWidth
- />
+ return <Popup icon={<FontAwesomeIcon size={'1x'} icon={icon} />} text={text} type={Type.TERT} color={color} popup={<SelectedDocView selectedDocs={selectedDocs} />} fillWidth />;
}
} else {
dropdown = false;
- return <Button
- text={`None Selected`}
- type={Type.TERT}
- color={color}
- fillWidth
- inactive
- />
+ return <Button text={`None Selected`} type={Type.TERT} color={color} fillWidth inactive />;
}
noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
- } else {
+ } else {
text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
- getStyle = (val: string) => { return { fontFamily: val } }
+ getStyle = (val: string) => {
+ return { fontFamily: val };
+ };
}
} catch (e) {
console.log(e);
}
- console.log("current item: ", text);
-
// Get items to place into the list
const list: IListItemProps[] = this.buttonList
.filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
- .map(value => (
- {
- text: value.charAt(0).toUpperCase() + value.slice(1),
- val: value,
- style: getStyle(value),
- onClick: undoable(() => script.script.run({ self: this.rootDoc, value }), value)
- // shortcut: '#',
- }
- ));
-
+ .map(value => ({
+ text: value.charAt(0).toUpperCase() + value.slice(1),
+ val: value,
+ style: getStyle(value),
+ onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
+ // shortcut: '#',
+ }));
return (
- <Dropdown
+ <Dropdown
selectedVal={text}
- setSelectedVal={undoable((val) => script.script.run({ self: this.rootDoc, val }), `dropdown select ${this.label}`)}
- color={color}
- type={isViewDropdown ? Type.TERT : Type.PRIM}
- dropdownType={DropdownType.SELECT}
+ setSelectedVal={undoable(val => script.script.run({ this: this.layoutDoc, self: this.rootDoc, val }), `dropdown select ${this.label}`)}
+ color={color}
+ type={isViewDropdown ? Type.TERT : Type.PRIM}
+ dropdownType={DropdownType.SELECT}
items={list}
tooltip={this.label}
fillWidth
/>
- )
+ );
}
-
@computed get colorScript() {
return ScriptCast(this.rootDoc.script);
}
@@ -290,14 +273,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
@computed get colorButton() {
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const curColor = this.colorScript?.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent';
+ const curColor = this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent';
const tooltip: string = StrCast(this.rootDoc.toolTip);
return (
- <ColorPicker
- setSelectedColor={(value) => {
+ <ColorPicker
+ setSelectedColor={value => {
const s = this.colorScript;
- s && undoable(() => s.script.run({ self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)();
+ s && undoable(() => s.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)();
}}
selectedColor={curColor}
type={Type.PRIM}
@@ -306,7 +289,35 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
tooltip={tooltip}
label={this.label}
/>
- )
+ );
+ }
+ @computed get multiToggleButton() {
+ // Determine the type of toggle button
+ const tooltip: string = StrCast(this.rootDoc.toolTip);
+
+ const script = ScriptCast(this.rootDoc.onClick);
+ const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
+ // Colors
+ const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+ const items = DocListCast(this.rootDoc.data);
+ return (
+ <MultiToggle
+ tooltip={`Toggle ${tooltip}`}
+ type={Type.PRIM}
+ color={color}
+ label={this.label}
+ items={DocListCast(this.rootDoc.data).map(item => ({
+ icon: <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={StrCast(item.icon) as any} color={color} />,
+ tooltip: StrCast(item.toolTip),
+ val: StrCast(item.toolType),
+ }))}
+ selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType)}
+ setSelectedVal={(val: string | number) => {
+ const itemDoc = items.find(item => item.toolType === val);
+ itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: val, _readOnly_: false });
+ }}
+ />
+ );
}
@computed get toggleButton() {
@@ -315,25 +326,24 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
const tooltip: string = StrCast(this.rootDoc.toolTip);
const script = ScriptCast(this.rootDoc.onClick);
- const toggleStatus = script ? script.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
+ const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
// Colors
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- console.log(tooltip, toggleStatus);
return (
- <Toggle
- tooltip={`Toggle ${tooltip}`}
- toggleType={ToggleType.BUTTON}
- type={Type.PRIM}
- toggleStatus={toggleStatus}
- text={buttonText}
- color={color}
- icon={this.Icon(color)!}
+ <Toggle
+ tooltip={`Toggle ${tooltip}`}
+ toggleType={ToggleType.BUTTON}
+ type={Type.PRIM}
+ toggleStatus={toggleStatus}
+ text={buttonText}
+ color={color}
+ icon={this.Icon(color)!}
label={this.label}
- onPointerDown={() => script.script.run({ self: this.rootDoc, value: !toggleStatus, _readOnly_: false })}
+ onPointerDown={() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })}
/>
- )
+ );
}
/**
@@ -344,79 +354,50 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
const tooltip: string = StrCast(this.rootDoc.toolTip);
- return (
- <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label}/>
- )
+ return <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label} />;
}
@computed get editableText() {
// Script for running the toggle
const script = ScriptCast(this.rootDoc.script);
// Function to run the script
- const checkResult = script?.script.run({ value: '', _readOnly_: true }).result;
+ const checkResult = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result;
+
+ const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value, _readOnly_: false }).result;
+
+ return <EditableText editing={false} setEditing={(editing: boolean) => {}} />;
- const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result;
-
- return <EditableText
- editing={false} setEditing={(editing: boolean) => {}}
- />
-
return (
<div className="menuButton editableText">
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} />
<div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}>
- <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
+ <EditableView GetValue={() => script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
</div>
</div>
);
}
render() {
- // determine dash button metadata
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
- const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const tooltip: string = StrCast(this.rootDoc.toolTip);
- const onClickScript = ScriptCast(this.rootDoc.onClick);
- const script = ScriptCast(this.rootDoc.script);
- // TODO:glr Add label of button type
- let button: JSX.Element = this.defaultButton;
+ const tooltip = StrCast(this.rootDoc.toolTip);
+ const scriptFunc = () => ScriptCast(this.rootDoc.onClick)?.script.run({ this: this.layoutDoc, self: this.rootDoc, _readOnly_: false });
+ const btnProps = { tooltip, icon: this.Icon(color)!, label: this.label };
// prettier-ignore
switch (this.type) {
- case ButtonType.EditableText:
- button = this.editableText;
- break;
- case ButtonType.DropdownList:
- button = this.dropdownListButton;
- break;
- case ButtonType.ColorButton:
- button = this.colorButton;
- break;
case ButtonType.NumberDropdownButton:
case ButtonType.NumberInlineButton:
- case ButtonType.NumberSliderButton:
- button = this.numberDropdown;
- break;
- case ButtonType.DropdownButton:
- button = this.dropdownButton;
- break;
- case ButtonType.ToggleButton: button = this.toggleButton; break;
+ case ButtonType.NumberSliderButton: return this.numberDropdown;
+ case ButtonType.EditableText: return this.editableText;
+ case ButtonType.DropdownList: return this.dropdownListButton;
+ case ButtonType.ColorButton: return this.colorButton;
+ case ButtonType.DropdownButton: return this.dropdownButton;
+ case ButtonType.MultiToggleButton: return this.multiToggleButton;
+ case ButtonType.ToggleButton: return this.toggleButton;
case ButtonType.ClickButton:
- case ButtonType.ToolButton:
- button = (
- <IconButton tooltip={tooltip} onPointerDown={() => onClickScript?.script.run({ _readOnly_: false })} color={color} icon={this.Icon(color)!} label={this.label}/>
- );
- break;
- case ButtonType.TextButton:
- button = (
- <Button tooltip={tooltip} icon={this.Icon(color)!} text={StrCast(this.rootDoc.buttonText)} label={this.label}/>
- );
- break;
- case ButtonType.MenuButton: button = (
- <IconButton tooltip={tooltip} onPointerDown={() => onClickScript?.script.run({ self: this.rootDoc, _readOnly_: false })} tooltipPlacement='right' size={Size.LARGE} color={color} icon={this.Icon(color)!} label={this.label}/>
- );
- break;
+ case ButtonType.ToolButton: return <IconButton {...btnProps} size={Size.LARGE} color={color} />;
+ case ButtonType.TextButton: return <Button {...btnProps} text={StrCast(this.rootDoc.buttonText)}/>;
+ case ButtonType.MenuButton: return <IconButton {...btnProps} color={color} size={Size.LARGE} tooltipPlacement='right' onPointerDown={scriptFunc} />;
}
-
- return button;
+ return this.defaultButton;
}
}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 86191de63..d69009415 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import wiki from 'wikijs';
import { Doc, DocCastAsync, Opt } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
+import { DirectLinks, Height, Width } from '../../../fields/DocSymbols';
import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
import { DocServer } from '../../DocServer';
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index de0b57fd7..4919ee94c 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -61,7 +61,6 @@ const script = document.createElement('script');
script.defer = true;
script.async = true;
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
-console.log(script.src);
document.head.appendChild(script);
/**
diff --git a/src/client/views/nodes/ScreenshotBox.scss b/src/client/views/nodes/ScreenshotBox.scss
index 6fb5ea7b3..1e9b64a0b 100644
--- a/src/client/views/nodes/ScreenshotBox.scss
+++ b/src/client/views/nodes/ScreenshotBox.scss
@@ -1,6 +1,5 @@
.screenshotBox {
transform-origin: top left;
- background: white;
color: black;
// .screenshotBox-viewer {
// opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger
@@ -12,46 +11,39 @@
#CANCAN {
canvas {
- width:100% !important;
+ width: 100% !important;
height: 100% !important;
}
}
-.screenshotBox-content, .screenshotBox-content-interactive, .screenshotBox-cont-fullScreen {
+.screenshotBox-content,
+.screenshotBox-content-interactive,
+.screenshotBox-cont-fullScreen {
width: 100%;
z-index: -1; // 0; // logically this should be 0 (or unset) which would give us transparent brush strokes over videos. However, this makes Chrome crawl to a halt
position: absolute;
}
-.screenshotBox-content, .screenshotBox-content-interactive, .screenshotBox-content-fullScreen {
- height: Auto;
+.screenshotBox-content,
+.screenshotBox-content-interactive,
+.screenshotBox-content-fullScreen {
+ height: Auto;
}
.screenshotBox-uiButtons {
- background:dimgray;
- border: orange solid 1px;
position: absolute;
right: 25;
top: 0;
- width:25;
+ width: 22;
height: 25;
- .screenshotBox-snapshot{
- color : white;
- top :0px;
- right : 5px;
- position: absolute;
- background-color:rgba(50, 50, 50, 0.2);
- transform-origin: left top;
- pointer-events:all;
- }
- .screenshotBox-recorder{
- color : white;
- top :0px;
+ .screenshotBox-recorder {
+ color: white;
+ top: 4px;
left: 5px;
position: absolute;
- background-color:rgba(50, 50, 50, 0.2);
+ background-color: rgba(50, 50, 50, 0.2);
transform-origin: left top;
- pointer-events:all;
+ pointer-events: all;
}
}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 312b3c619..271ff3cf8 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -25,6 +25,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import './ScreenshotBox.scss';
import { VideoBox } from './VideoBox';
+import { SettingsManager } from '../../util/SettingsManager';
declare class MediaRecorder {
constructor(e: any, options?: any); // whatever MediaRecorder has
@@ -224,7 +225,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
const aud_chunks: any = [];
this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
this._audioRec.onstop = async (e: any) => {
- const [{ result }] = await Networking.UploadFilesToServer(aud_chunks.map((file: any) => ({file})));
+ const [{ result }] = await Networking.UploadFilesToServer(aud_chunks.map((file: any) => ({ file })));
if (!(result instanceof Error)) {
this.dataDoc[this.props.fieldKey + '-audio'] = new AudioField(result.accessPaths.agnostic.client);
}
@@ -235,9 +236,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
this._videoRec.onstart = () => (this.dataDoc[this.props.fieldKey + '-recordingStart'] = new DateField(new Date()));
this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
this._videoRec.onstop = async (e: any) => {
- console.log('screenshotbox: upload');
const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
- const [{ result }] = await Networking.UploadFilesToServer({file});
+ const [{ result }] = await Networking.UploadFilesToServer({ file });
this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
if (!(result instanceof Error)) {
// convert this screenshotBox into normal videoBox
@@ -313,7 +313,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
</>
</CollectionFreeFormView>
</div>
- <div style={{ background: 'white', position: 'relative', height: this.formattedPanelHeight() }}>
+ <div style={{ background: SettingsManager.Instance.userColor, position: 'relative', height: this.formattedPanelHeight() }}>
{!(this.dataDoc[this.fieldKey + '-dictation'] instanceof Doc) ? null : (
<FormattedTextBox
{...this.props}
@@ -335,8 +335,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
</div>
</div>
{!this.props.isSelected() ? null : (
- <div className="screenshotBox-uiButtons">
- <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording}>
+ <div className="screenshotBox-uiButtons" style={{ background: SettingsManager.Instance.userColor }}>
+ <div className="screenshotBox-recorder" style={{ color: SettingsManager.Instance.userBackgroundColor, background: SettingsManager.Instance.userVariantColor }} key="snap" onPointerDown={this.toggleRecording}>
<FontAwesomeIcon icon="file" size="lg" />
</div>
</div>
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 3ad3c911d..7c8a1849e 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -610,7 +610,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
' ': {
dataProvider: (token: any) => this.handleToken(token),
component: (blob: any) => {
- console.log('Blob', blob);
return this.renderFuncListElement(blob.entity);
},
output: (item: any, trigger: any) => {
@@ -621,7 +620,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
'.': {
dataProvider: (token: any) => this.handleToken(token),
component: (blob: any) => {
- console.log('Blob', blob);
return this.renderFuncListElement(blob.entity);
},
output: (item: any, trigger: any) => {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 5cdd6b5f2..07b931312 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -125,11 +125,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._searchRef.current?.setRangeText(searchString);
});
}
- if (clear) {
- this._iframe?.contentWindow?.getSelection()?.empty();
- }
- if (searchString) {
- (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true);
+ try {
+ if (clear) {
+ this._iframe?.contentWindow?.getSelection()?.empty();
+ }
+ if (searchString) {
+ (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true);
+ }
+ } catch (e) {
+ console.log("WebBox search error", e)
}
return true;
};
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index b4fb7a44e..d5ad128fe 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -17,6 +17,7 @@ import { OpenWhere } from '../DocumentView';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
import React = require('react');
+import { Transform } from '../../../util/Transform';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
@@ -113,6 +114,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
componentWillUnmount() {
this._reactionDisposer?.();
}
+ return100 = () => 100;
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
@@ -123,7 +125,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
col={0}
deselectCell={emptyFunction}
selectCell={emptyFunction}
- maxWidth={this.props.hideKey ? undefined : () => 100}
+ maxWidth={this.props.hideKey ? undefined : this.return100}
columnWidth={this.props.hideKey ? () => this.props.tbox.props.PanelWidth() - 20 : returnZero}
selectedCell={() => [this._dashDoc!, 0]}
fieldKey={this._fieldKey}
@@ -135,6 +137,8 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
allowCRs={true}
oneLine={!this._expanded}
finishEdit={action(() => (this._expanded = false))}
+ transform={Transform.Identity}
+ menuTarget={null}
/>
</div>
);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 109b62e6f..348bdd79e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -84,7 +84,6 @@ audiotag:hover {
height: 11;
}
-.formattedTextBox-outer-selected,
.formattedTextBox-outer {
position: relative;
overflow: auto;
@@ -92,9 +91,6 @@ audiotag:hover {
width: 100%;
height: unset;
}
-.formattedTextBox-outer-selected {
- cursor: text;
-}
.formattedTextBox-sidebar-handle {
position: absolute;
@@ -148,10 +144,8 @@ audiotag:hover {
}
.formattedTextBox-inner-rounded,
-.formattedTextBox-inner-rounded-selected,
.formattedTextBox-inner,
-.formattedTextBox-inner-minimal,
-.formattedTextBox-inner-selected {
+.formattedTextBox-inner-minimal {
height: 100%;
white-space: pre-wrap;
.ProseMirror:hover {
@@ -169,17 +163,6 @@ audiotag:hover {
border-width: 1px;
}
}
-.formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner-selected {
- > .ProseMirror {
- padding: 10px;
- }
-}
-.formattedTextBox-outer-selected {
- > .ProseMirror:hover {
- background: unset;
- }
-}
.gpt-typing-wrapper {
padding: 10px;
@@ -640,7 +623,6 @@ footnote::before {
}
}
- .formattedTextBox-outer-selected,
.formattedTextBox-outer {
position: relative;
overflow: auto;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 072977150..332f0f467 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -97,6 +97,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
public _applyingChange: string = '';
+ private _finishingLink = false;
private _searchIndex = 0;
private _lastTimedMark: Mark | undefined = undefined;
private _cachedLinks: Doc[] = [];
@@ -246,7 +247,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!pinProps && this._editorView?.state.selection.empty) return this.rootDoc;
const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.rootDoc.title), annotationOn: this.rootDoc });
this.addDocument(anchor);
+ this._finishingLink = true;
this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
+ this._finishingLink = false;
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true } }, this.rootDoc);
return anchor;
};
@@ -327,7 +330,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
if ((!prevData && !protoData) || newText || (!newText && !templateData)) {
// if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
- if (this.props.isContentActive() && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
+ if ((this._finishingLink || this.props.isContentActive()) && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
const numstring = NumCast(dataDoc[this.fieldKey], null);
dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText);
dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited
@@ -458,8 +461,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
alink =
alink ??
- (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)) ||
- DocUtils.MakeLink(this.props.Document, target, { link_relationship: LinkManager.AutoKeywords })!);
+ (LinkManager.Links(this.rootDoc).find(
+ link =>
+ Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && //
+ Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)
+ ) ||
+ DocUtils.MakeLink(this.rootDoc, target, { link_relationship: LinkManager.AutoKeywords })!);
newAutoLinks.add(alink);
const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
@@ -841,7 +848,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const appearance = cm.findByDescription('Appearance...');
const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
- appearanceItems.push({ description: 'Change Perspective...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
+ appearanceItems.push({ description: 'Change Style...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
// this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
!Doc.noviceMode &&
@@ -1589,7 +1596,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
);
};
+ @action
componentWillUnmount() {
+ if (this._recording) {
+ this._recording = !this._recording;
+ }
Object.values(this._disposers).forEach(disposer => disposer?.());
this.endUndoTypingBatch();
FormattedTextBox.LiveTextUndo?.end();
@@ -1628,8 +1639,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
if (this._recording && !e.ctrlKey && e.button === 0) {
this.breakupDictation();
- e.preventDefault();
- e.stopPropagation();
}
this._downX = e.clientX;
this._downY = e.clientY;
@@ -2026,12 +2035,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
cycleAlternateText = () => {
if (this.layoutDoc._layout_enableAltContentUI) {
- const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+ const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
}
};
@computed get overlayAlternateIcon() {
- const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+ const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
return (
<Tooltip
title={
@@ -2075,17 +2084,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
};
_oldWheel: any;
+ @computed get fontColor() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+ }
+ @computed get fontSize() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize);
+ }
+ @computed get fontFamily() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily);
+ }
+ @computed get fontWeight() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight);
+ }
render() {
TraceMobx();
- const active = this.props.isContentActive();
const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : '';
- if (!active && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
- const minimal = this.props.ignoreAutoHeight;
+ setTimeout(() => !this.props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
- const selPad = (active && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
- const selPaddingClass = active && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : '';
const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
return styleFromLayoutString?.height === '0px' ? null : (
<div
@@ -2102,23 +2119,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
? {}
: {
transform: `scale(${scale})`,
- transformOrigin: 'top left',
width: `${100 / scale}%`,
height: `${100 / scale}%`,
}),
- display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
+ // display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
transition: 'inherit',
// overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined,
- color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
- fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
- fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily),
- fontWeight: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight),
+ color: this.fontColor,
+ fontSize: this.fontSize,
+ fontFamily: this.fontFamily,
+ fontWeight: this.fontWeight,
...styleFromLayoutString,
}}>
<div
className="formattedTextBox-cont"
ref={this._ref}
style={{
+ cursor: this.props.isContentActive() ? 'text' : undefined,
overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none',
@@ -2133,7 +2150,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onPointerDown={this.onPointerDown}
onDoubleClick={this.onDoubleClick}>
<div
- className={`formattedTextBox-outer${active ? '-selected' : ''}`}
+ className={`formattedTextBox-outer`}
ref={this._scrollRef}
style={{
width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
@@ -2142,15 +2159,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onScroll={this.onScroll}
onDrop={this.ondrop}>
<div
- className={minimal ? 'formattedTextBox-minimal' : `formattedTextBox-inner${rounded}${selPaddingClass}`}
+ className={`formattedTextBox-inner${rounded}`}
ref={this.createDropTarget}
style={{
padding: StrCast(this.layoutDoc._textBoxPadding),
- paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
- paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
- paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
- paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
- // pointerEvents: !active && IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined,
+ paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX}px`),
+ paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX}px`),
+ paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
+ paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
}}
/>
</div>
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index ac1e7ce5d..8bafc2cef 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -243,13 +243,13 @@ export class RichTextRules {
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
// [[<fieldKey> : <Doc>]]
- // [[:Doc]] => hyperlink
+ // [[:docTitle]] => hyperlink
// [[fieldKey]] => show field
// [[fieldKey=value]] => show field and also set its value
- // [[fieldKey:Doc]] => show field of doc
+ // [[fieldKey:docTitle]] => show field of doc
new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
const fieldKey = match[1];
- const docId = match[3]?.replace(':', '');
+ const docTitle = match[3]?.replace(':', '');
const value = match[2]?.substring(1);
const linkToDoc = (target: Doc) => {
const rstate = this.TextBox.EditorView?.state;
@@ -266,12 +266,12 @@ export class RichTextRules {
}
};
if (!fieldKey) {
- if (docId) {
- const target = DocServer.QUERY_SERVER_CACHE(docId);
- if (target) setTimeout(() => linkToDoc(target));
- else DocServer.GetRefField(docId).then(docx => linkToDoc((docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docId + '(auto)', _width: 500, _height: 500 }, docId)));
-
- return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+ if (docTitle) {
+ const target = DocServer.FindDocByTitle(docTitle);
+ if (target) {
+ setTimeout(() => linkToDoc(target));
+ return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+ }
}
return state.tr;
}
@@ -279,8 +279,12 @@ export class RichTextRules {
const num = value.match(/^[0-9.]$/);
this.Document[DocData][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
}
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId, hideKey: false });
- return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
+ const target = DocServer.FindDocByTitle(docTitle);
+ if (target) {
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target[Id], hideKey: false });
+ return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
+ }
+ return state.tr;
}),
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx
index a5d21cc8e..58f0b29e4 100644
--- a/src/client/views/nodes/importBox/ImportElementBox.tsx
+++ b/src/client/views/nodes/importBox/ImportElementBox.tsx
@@ -1,13 +1,13 @@
-import { observer } from "mobx-react";
-import { ViewBoxBaseComponent } from "../../DocComponent";
-import { FieldView, FieldViewProps } from "../FieldView";
-import { computed } from "mobx";
-import { Id } from "../../../../fields/FieldSymbols";
-import React from "react";
-import { EditableView } from "../../EditableView";
-import { DocListCast } from "../../../../fields/Doc";
-import { StrCast } from "../../../../fields/Types";
-
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../../fields/Doc';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../../Utils';
+import { Transform } from '../../../util/Transform';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { DefaultStyleProvider } from '../../StyleProvider';
+import { DocumentView, DocumentViewInternal } from '../DocumentView';
+import { FieldView, FieldViewProps } from '../FieldView';
+import React = require('react');
@observer
export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -15,56 +15,24 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
return FieldView.LayoutString(ImportElementBox, fieldKey);
}
- private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
- private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
- private _titleRef: React.RefObject<EditableView> = React.createRef();
-
- @computed importBoxVoew() {
- return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as PresBox;
- }
-
- @computed get indexInPres() {
- return DocListCast(this.presBox?.[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc);
+ screenToLocalXf = () => this.props.ScreenToLocalTransform().scale(1 * (this.props.NativeDimScaling?.() || 1));
+ @computed get mainItem() {
+ return (
+ <div style={{ backgroundColor: 'pink' }}>
+ <DocumentView
+ {...this.props} //
+ LayoutTemplateString={undefined}
+ Document={this.rootDoc}
+ isContentActive={returnFalse}
+ DataDoc={undefined}
+ addDocument={returnFalse}
+ ScreenToLocalTransform={this.screenToLocalXf}
+ hideResizeHandles={true}
+ />
+ </div>
+ );
}
-
- @computed get presBox() {
- return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
+ render() {
+ return !(this.rootDoc instanceof Doc) ? null : this.mainItem;
}
-
- // @computed get selectedArray() {
- // return this.presBoxView?.selectedArray;
- // }
-
-@computed get mainItem() {
- const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres;
- //const isSelected: boolean = this.selectedArray?.has(this.rootDoc) ? true : false;
- // const activeItem: Doc = this.rootDoc;
-
- return(
- <div
- className = {`presItem-container`}
- // key={this.props.Document[Id] + this.indexInPres}
- style = {{backgroundColor: 'pink'}}
-
- >
- <div
- ref = {this._dragRef}
- className = {`presItem-slide ${isCurrent ? 'active' : ''}`}
- style = {{
- backgroundColor: 'green'
- }}>
- <div
- className="presItem-number"
- title = "select without navigation"
-
- >
- {/* <EditableView ref={this._titleRef} oneLine={true} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} /> */}
-
- </div>
- </div>
-
- </div>
- )
}
-
-} \ No newline at end of file
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index dd8cf7002..7404650d6 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -17,6 +17,7 @@ import { EditorView } from 'prosemirror-view';
import './AnchorMenu.scss';
import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { StrCast } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -241,7 +242,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
colorPicker={this.highlightColor}
color={StrCast(Doc.UserDoc().userColor)}
/>
- <ColorPicker colorPickerType={'github'} selectedColor={this.highlightColor} setSelectedColor={color => this.changeHighlightColor(color)} size={Size.XSMALL} />
+ <ColorPicker selectedColor={this.highlightColor} setSelectedColor={this.changeHighlightColor} size={Size.XSMALL} />
</Group>
);
}
@@ -266,7 +267,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
canSummarize = (): boolean => {
const docs = SelectionManager.Docs();
if (docs.length > 0) {
- return docs.some(doc => doc.type === 'pdf' || doc.type === 'web');
+ return docs.some(doc => doc.type === DocumentType.PDF || doc.type === DocumentType.WEB);
}
return false;
};
@@ -288,31 +289,74 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
this.Status === 'marquee' ? (
<>
{this.highlighter}
- <IconButton tooltip={'Drag to Place Annotation'} onPointerDown={this.pointerDown} icon={<FontAwesomeIcon icon="comment-alt" />} color={StrCast(Doc.UserDoc().userColor)} />
+ <IconButton
+ tooltip="Drag to Place Annotation" //
+ onPointerDown={this.pointerDown}
+ icon={<FontAwesomeIcon icon="comment-alt" />}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
{/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection*/}
{AnchorMenu.Instance.StartCropDrag === unimplementedFunction && this.canSummarize() && (
- <Tooltip key="gpt" title={<div className="dash-tooltip">Summarize with AI</div>}>
- <button className="antimodeMenu-button annotate" onPointerDown={this.gptSummarize} style={{ cursor: 'grab' }}>
- <FontAwesomeIcon icon="comment-dots" size="lg" />
- </button>
- </Tooltip>
+ <IconButton
+ tooltip="Summarize with AI" //
+ onPointerDown={this.gptSummarize}
+ icon={<FontAwesomeIcon icon="comment-dots" size="lg" />}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
)}
{AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
- <IconButton tooltip={'Click to Record Annotation'} onPointerDown={this.audioDown} icon={<FontAwesomeIcon icon="microphone" />} color={StrCast(Doc.UserDoc().userColor)} />
+ <IconButton
+ tooltip="Click to Record Annotation" //
+ onPointerDown={this.audioDown}
+ icon={<FontAwesomeIcon icon="microphone" />}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
)}
{/* Removed text editing for now, not quite ready */}
{/* {this.canEdit() && <IconButton tooltip={'AI edit suggestions'} onPointerDown={this.gptEdit} icon={<FontAwesomeIcon icon="pencil-alt" />} color={StrCast(Doc.UserDoc().userColor)} />} */}
{<IconButton tooltip={'Generate DALL-E Image'} onPointerDown={this.gptImage} icon={<FontAwesomeIcon icon="image" />} color={StrCast(Doc.UserDoc().userColor)} />}
- <Popup tooltip="Find document to link to selected text" type={Type.PRIM} icon={<FontAwesomeIcon icon={'search'} />} popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />} color={StrCast(Doc.UserDoc().userColor)} />
+ <Popup
+ tooltip="Find document to link to selected text" //
+ type={Type.PRIM}
+ icon={<FontAwesomeIcon icon={'search'} />}
+ popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
{AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
- <IconButton tooltip={'Click/Drag to create cropped image'} onPointerDown={this.cropDown} icon={<FontAwesomeIcon icon="image" />} color={StrCast(Doc.UserDoc().userColor)} />
+ <IconButton
+ tooltip="Click/Drag to create cropped image" //
+ onPointerDown={this.cropDown}
+ icon={<FontAwesomeIcon icon="image" />}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
)}
</>
) : (
<>
- {this.Delete !== returnFalse && <IconButton tooltip={'Remove Link Anchor'} onPointerDown={this.Delete} icon={<FontAwesomeIcon icon="trash-alt" />} color={StrCast(Doc.UserDoc().userColor)} />}
- {this.PinToPres !== returnFalse && <IconButton tooltip={'Pin to Presentation'} onPointerDown={this.PinToPres} icon={<FontAwesomeIcon icon="map-pin" />} color={StrCast(Doc.UserDoc().userColor)} />}
- {this.ShowTargetTrail !== returnFalse && <IconButton tooltip={'Show Linked Trail'} onPointerDown={this.ShowTargetTrail} icon={<FontAwesomeIcon icon="taxi" />} color={StrCast(Doc.UserDoc().userColor)} />}
+ {this.Delete !== returnFalse && (
+ <IconButton
+ tooltip="Remove Link Anchor" //
+ onPointerDown={this.Delete}
+ icon={<FontAwesomeIcon icon="trash-alt" />}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
+ )}
+ {this.PinToPres !== returnFalse && (
+ <IconButton
+ tooltip="Pin to Presentation" //
+ onPointerDown={this.PinToPres}
+ icon={<FontAwesomeIcon icon="map-pin" />}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
+ )}
+ {this.ShowTargetTrail !== returnFalse && (
+ <IconButton
+ tooltip="Show Linked Trail" //
+ onPointerDown={this.ShowTargetTrail}
+ icon={<FontAwesomeIcon icon="taxi" />}
+ color={StrCast(Doc.UserDoc().userColor)}
+ />
+ )}
{this.IsTargetToggler !== returnFalse && (
<Toggle
tooltip={'Make target visibility toggle on click'}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index e911bd283..1ceea697a 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -18,6 +18,7 @@ import './SearchBox.scss';
import { fetchRecommendations } from '../newlightbox/utils';
import { IRecommendation, Recommendation } from '../newlightbox/components';
import { Colors } from '../global/globalEnums';
+import { SettingsManager } from '../../util/SettingsManager';
const DAMPENING_FACTOR = 0.9;
const MAX_ITERATIONS = 25;
@@ -218,7 +219,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
}
@action
static staticSearchCollection(rootDoc: Opt<Doc>, query: string) {
- const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
+ const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.SEARCH, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
const blockedKeys = [
'x',
'y',
@@ -398,22 +399,21 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
if (query) {
this.searchCollection(query);
- const response = await fetchRecommendations('', query, [], true)
- const recs = response.recommendations
- const recommendations:IRecommendation[] = []
+ const response = await fetchRecommendations('', query, [], true);
+ const recs = response.recommendations;
+ const recommendations: IRecommendation[] = [];
for (const key in recs) {
const title = recs[key].title;
- console.log(title);
- const url = recs[key].url
- const type = recs[key].type
- const text = recs[key].text
- const transcript = recs[key].transcript
- const previewUrl = recs[key].previewUrl
- const embedding = recs[key].embedding
- const distance = recs[key].distance
- const source = recs[key].source
- const related_concepts = recs[key].related_concepts
- const docId = recs[key].doc_id
+ const url = recs[key].url;
+ const type = recs[key].type;
+ const text = recs[key].text;
+ const transcript = recs[key].transcript;
+ const previewUrl = recs[key].previewUrl;
+ const embedding = recs[key].embedding;
+ const distance = recs[key].distance;
+ const source = recs[key].source;
+ const related_concepts = recs[key].related_concepts;
+ const docId = recs[key].doc_id;
recommendations.push({
title: title,
data: url,
@@ -425,11 +425,11 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
distance: Math.round(distance * 100) / 100,
source: source,
related_concepts: related_concepts,
- docId: docId
- })
+ docId: docId,
+ });
}
- const setRecommendations = action(() => this._recommendations = recommendations)
- setRecommendations()
+ const setRecommendations = action(() => (this._recommendations = recommendations));
+ setRecommendations();
}
};
@@ -439,6 +439,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
*/
resetSearch = action(() => {
this._results.forEach((_, doc) => {
+ DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true);
Doc.UnBrushDoc(doc);
Doc.UnHighlightDoc(doc);
Doc.ClearSearchMatches();
@@ -461,7 +462,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
*/
@computed
public get selectOptions() {
- const selectValues = ['all', 'rtf', 'image', 'pdf', 'web', 'video', 'audio', 'collection'];
+ const selectValues = ['all', DocumentType.RTF, DocumentType.IMG, DocumentType.PDF, DocumentType.WEB, DocumentType.VID, DocumentType.AUDIO, DocumentType.COL];
return selectValues.map(value => (
<option key={value} value={value}>
@@ -517,19 +518,19 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
className={className}>
<div className="searchBox-result-title">{title as string}</div>
<div className="searchBox-result-type">{formattedType}</div>
- <div className="searchBox-result-keys">{result[1].join(', ')}</div>
+ <div className="searchBox-result-keys" style={{ color: SettingsManager.Instance.userVariantColor }}>
+ {result[1].join(', ')}
+ </div>
</div>
</Tooltip>
);
}
});
- const recommendationsJSX: JSX.Element[] = this._recommendations.map((props) => (
- <Recommendation {...props}/>
- ))
+ const recommendationsJSX: JSX.Element[] = this._recommendations.map(props => <Recommendation {...props} />);
return (
- <div className="searchBox-container" style={{pointerEvents: 'all', background: StrCast(Doc.UserDoc().userBackgroundColor)}}>
+ <div className="searchBox-container" style={{ pointerEvents: 'all', color: SettingsManager.Instance.userColor, background: SettingsManager.Instance.userBackgroundColor }}>
<div className="searchBox-bar">
{isLinkSearch ? null : (
<select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
@@ -552,20 +553,24 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
ref={this._inputRef}
/>
</div>
- {resultsJSX.length > 0 && <div className="searchBox-results-container">
- <div className="section-header" style={{background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}}>
- <div className="section-title">Results</div>
- <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ {resultsJSX.length > 0 && (
+ <div className="searchBox-results-container">
+ <div className="section-header" style={{ background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE) }}>
+ <div className="section-title">Results</div>
+ <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ </div>
+ <div className="searchBox-results-view">{resultsJSX}</div>
</div>
- <div className="searchBox-results-view">{resultsJSX}</div>
- </div>}
- {recommendationsJSX.length > 0 && <div className="searchBox-recommendations-container">
- <div className="section-header" style={{background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}}>
- <div className="section-title">Recommendations</div>
- <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ )}
+ {recommendationsJSX.length > 0 && (
+ <div className="searchBox-recommendations-container">
+ <div className="section-header" style={{ background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE) }}>
+ <div className="section-title">Recommendations</div>
+ <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ </div>
+ <div className="searchBox-recommendations-view">{recommendationsJSX}</div>
</div>
- <div className="searchBox-recommendations-view">{recommendationsJSX}</div>
- </div>}
+ )}
</div>
);
}
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index cb8eda9de..c194ede32 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -5,7 +5,7 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { FaBug, FaCamera, FaStamp } from 'react-icons/fa';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { AclAdmin } from '../../../fields/DocSymbols';
+import { AclAdmin, DashVersion } from '../../../fields/DocSymbols';
import { StrCast } from '../../../fields/Types';
import { GetEffectiveAcl } from '../../../fields/util';
import { DocumentManager } from '../../util/DocumentManager';
@@ -21,6 +21,7 @@ import { MainView } from '../MainView';
import { CollectionDockingView } from '../collections/CollectionDockingView';
import { Colors } from '../global/globalEnums';
import './TopBar.scss';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
/**
* ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user
@@ -35,10 +36,14 @@ export class TopBar extends React.Component {
});
};
- @computed get color() { return StrCast(Doc.UserDoc().userColor, Colors.LIGHT_GRAY); }
- @computed get variantColor() { return StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE); }
+ @computed get color() {
+ return StrCast(Doc.UserDoc().userColor, Colors.LIGHT_GRAY);
+ }
+ @computed get variantColor() {
+ return StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE);
+ }
@computed get backgroundColor() {
- return PingManager.Instance.IsBeating ? StrCast(Doc.UserDoc().userBackgroundColor, Colors.DARK_GRAY) : Colors.MEDIUM_GRAY;
+ return PingManager.Instance.IsBeating ? SettingsManager.Instance.userBackgroundColor : Colors.MEDIUM_GRAY;
}
@observable happyHeart: boolean = PingManager.Instance.IsBeating;
@@ -102,7 +107,7 @@ export class TopBar extends React.Component {
tooltip="Open Dashboards"
size={Size.SMALL}
color={this.color}
- style={{fontWeight: 700, fontSize: '1rem'}}
+ style={{ fontWeight: 700, fontSize: '1rem' }}
onClick={(e: React.MouseEvent) => {
const dashView = Doc.ActiveDashboard && DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard);
ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' });
@@ -132,9 +137,10 @@ export class TopBar extends React.Component {
* and allows the user to access their account settings etc.
*/
@computed get topbarRight() {
+ const upToDate = DashVersion === CurrentUserUtils.ServerVersion;
return (
<div className="topbar-right">
- {Doc.ActiveDashboard ?
+ {Doc.ActiveDashboard ? (
<Button
text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'}
type={Type.TERT}
@@ -143,16 +149,16 @@ export class TopBar extends React.Component {
SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
}}
/>
- : null }
- <IconButton tooltip={"Issue Reporter ⌘I"} size={Size.SMALL} color={this.color} onClick={ReportManager.Instance.open} icon={<FaBug />} />
- <IconButton tooltip={"Documentation ⌘D"} size={Size.SMALL} color={this.color} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
- <IconButton tooltip={"Settings ⌘⇧S"} size={Size.SMALL} color={this.color} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} />
+ ) : null}
+ <IconButton tooltip={'Issue Reporter ⌘I'} size={Size.SMALL} color={this.color} onClick={ReportManager.Instance.open} icon={<FaBug />} />
+ <IconButton tooltip={'Documentation ⌘D'} size={Size.SMALL} color={this.color} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
+ <IconButton tooltip={'Settings ⌘⇧S'} size={Size.SMALL} color={this.color} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} />
<IconButton
size={Size.SMALL}
onClick={ServerStats.Instance.open}
type={Type.TERT}
- tooltip={'Server is ' + (PingManager.Instance.IsBeating ? '' : 'NOT ') + 'running'}
- color={this.happyHeart ? Colors.LIGHT_BLUE : Colors.ERROR_RED}
+ tooltip={'Server is ' + (PingManager.Instance.IsBeating ? '' : 'NOT ') + 'running ' + (upToDate ? DashVersion : 'out of date version:' + DashVersion)}
+ color={this.happyHeart ? (upToDate ? Colors.LIGHT_BLUE : Colors.YELLOW) : Colors.ERROR_RED}
icon={<FontAwesomeIcon icon={this.happyHeart ? 'heart' : 'heart-broken'} />}
/>
{/* <Button text={'Logout'} borderRadius={5} hoverStyle={'gray'} backgroundColor={Colors.DARK_GRAY} color={this.color} fontSize={FontSize.SECONDARY} onClick={() => window.location.assign(Utils.prepend('/logout'))} /> */}
@@ -163,12 +169,14 @@ export class TopBar extends React.Component {
render() {
return (
//TODO:glr Add support for light / dark mode
- <div style={{
- pointerEvents: 'all',
- color: this.color,
- background: this.backgroundColor,
- // borderColor: this.color
- }} className="topbar-container">
+ <div
+ style={{
+ pointerEvents: 'all',
+ color: this.color,
+ background: this.backgroundColor,
+ // borderColor: this.color
+ }}
+ className="topbar-container">
<div className="topbar-inner-container">
{this.topbarLeft}
{this.topbarCenter}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5a8a6e4b6..eb52cff88 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -238,22 +238,6 @@ export class Doc extends RefField {
public static get MyFilesystem() {
return DocCast(Doc.UserDoc().myFilesystem);
}
- public static get MyFileOrphans() {
- return DocCast(Doc.UserDoc().myFileOrphans);
- }
- public static AddFileOrphan(doc: Doc) {
- if (
- doc &&
- Doc.MyFileOrphans instanceof Doc &&
- Doc.IsDataProto(doc) &&
- !Doc.IsSystem(doc) &&
- ![DocumentType.CONFIG, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
- !doc.isFolder &&
- !doc.annotationOn
- ) {
- Doc.AddDocToList(Doc.MyFileOrphans, undefined, doc);
- }
- }
public static get MyTools() {
return DocCast(Doc.UserDoc().myTools);
}
@@ -388,7 +372,7 @@ export class Doc extends RefField {
} else {
return Cast(layoutField, Doc, null);
}
- return Cast(self[renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
+ return Cast(self[renderFieldKey + '_layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
}
return undefined;
}
@@ -573,7 +557,7 @@ export namespace Doc {
// compare whether documents or their protos match
export function AreProtosEqual(doc?: Doc, other?: Doc) {
- return doc && other && Doc.GetProto(doc) === Doc.GetProto(other);
+ return doc && other && (doc === other || Doc.GetProto(doc) === Doc.GetProto(other));
}
// Gets the data document for the document. Note: this is mis-named -- it does not specifically
@@ -701,7 +685,8 @@ export namespace Doc {
}
export function BestEmbedding(doc: Doc) {
- const bestEmbedding = Doc.GetProto(doc) ? DocListCast(doc.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+ const bestEmbedding = Doc.GetProto(doc) ? [doc, ...DocListCast(doc.proto_embeddings)].find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+ bestEmbedding && Doc.AddDocToList(Doc.GetProto(doc), 'protoEmbeddings', doc);
return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
@@ -782,7 +767,6 @@ export namespace Doc {
copy.cloneOf = doc;
cloneMap.set(doc[Id], copy);
- Doc.AddFileOrphan(copy);
return copy;
}
export function repairClone(clone: Doc, cloneMap: Map<string, Doc>, visited: Set<Doc>) {
@@ -917,7 +901,7 @@ export namespace Doc {
// If it doesn't find the expanded layout, then it makes a delegate of the template layout and
// saves it on the data doc indexed by the template layout's id.
//
- const expandedLayoutFieldKey = templateField + '-layout[' + templateLayoutDoc[Id] + ']';
+ const expandedLayoutFieldKey = templateField + '_layout[' + templateLayoutDoc[Id] + ']';
let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
@@ -985,6 +969,59 @@ export namespace Doc {
return overwrite;
}
+ export function FindReferences(infield: Doc | List<any>, references: Set<Doc>, system: boolean | undefined) {
+ if (infield instanceof List<any>) {
+ infield.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
+ return;
+ }
+ const doc = infield as Doc;
+ if (references.has(doc)) {
+ references.add(doc);
+ return;
+ }
+ const excludeLists = doc.title === 'My Recently Closed' || doc.title === 'My Header Bar' || doc.title === 'My Dashboards';
+ if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) return;
+ references.add(doc);
+ Object.keys(doc).forEach(key => {
+ if (key === 'proto') {
+ if (doc.proto instanceof Doc) {
+ Doc.FindReferences(doc.proto, references, system);
+ }
+ } else {
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
+ if (field instanceof RefField) {
+ if (field instanceof Doc) {
+ if (key === 'myLinkDatabase') {
+ field instanceof Doc && references.add(field);
+ // skip docs that have been closed and are scheduled for garbage collection
+ } else {
+ Doc.FindReferences(field, references, system);
+ }
+ }
+ } else if (cfield instanceof ComputedField) {
+ } else if (field instanceof ObjectField) {
+ if (field instanceof Doc) {
+ Doc.FindReferences(field, references, system);
+ } else if (field instanceof List) {
+ !excludeLists && Doc.FindReferences(field, references, system);
+ } else if (field instanceof ProxyField) {
+ if (key === 'myLinkDatabase') {
+ field instanceof Doc && references.add(field);
+ // skip docs that have been closed and are scheduled for garbage collection
+ } else {
+ Doc.FindReferences(field.value, references, system);
+ }
+ } else if (field instanceof PrefetchProxy) {
+ Doc.FindReferences(field.value, references, system);
+ }
+ } else if (field instanceof Promise) {
+ debugger; //This shouldn't happend...
+ }
+ }
+ });
+ }
+
export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
const copy = new Doc(copyProtoId, true);
updateCachedAcls(copy);
@@ -1025,7 +1062,6 @@ export namespace Doc {
if (retitle) {
copy.title = incrementTitleCopy(StrCast(copy.title));
}
- Doc.AddFileOrphan(copy);
return copy;
}
@@ -1044,7 +1080,6 @@ export namespace Doc {
if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate);
title && (delegate.title = title);
delegate[Initializing] = false;
- Doc.AddFileOrphan(delegate);
return delegate;
}
return undefined;
@@ -1177,7 +1212,7 @@ export namespace Doc {
// the document containing the view layout information - will be the Document itself unless the Document has
// a layout field or 'layout' is given.
export function Layout(doc: Doc, layout?: Doc): Doc {
- const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}-layout[` + layout[Id] + ']'], Doc, null);
+ const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}_layout[` + layout[Id] + ']'], Doc, null);
return overrideLayout || doc[DocLayout] || doc;
}
export function SetLayout(doc: Doc, layout: Doc | string) {
@@ -1309,7 +1344,7 @@ export namespace Doc {
}
export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) {
- if (linkDoc.link_anchor_2 === anchorDoc || (linkDoc.link_anchor_2 as Doc).annotationOn) return '2';
+ if (Doc.AreProtosEqual(linkDoc.link_anchor_2 as Doc, anchorDoc) || Doc.AreProtosEqual((linkDoc.link_anchor_2 as Doc).annotationOn as Doc, anchorDoc)) return '2';
return Doc.AreProtosEqual(anchorDoc, (linkDoc.link_anchor_1 as Doc).annotationOn as Doc) || Doc.AreProtosEqual(anchorDoc, linkDoc.link_anchor_1 as Doc) ? '1' : '2';
}
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index 66d1ab094..dc9d1084b 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -23,3 +23,5 @@ export const UpdatingFromServer = Symbol('DocUpdatingFromServer');
export const Initializing = Symbol('DocInitializing');
export const ForceServerWrite = Symbol('DocForceServerWrite');
export const CachedUpdates = Symbol('DocCachedUpdates');
+
+export const DashVersion = 'v0.5.4';
diff --git a/src/fields/FieldLoader.tsx b/src/fields/FieldLoader.tsx
index 2a7b936f7..a5a71833c 100644
--- a/src/fields/FieldLoader.tsx
+++ b/src/fields/FieldLoader.tsx
@@ -6,10 +6,9 @@ import './FieldLoader.scss';
@observer
export class FieldLoader extends React.Component {
- @observable public static ServerLoadStatus = { requested: 0, retrieved: 0 };
- public static active = false;
+ @observable public static ServerLoadStatus = { requested: 0, retrieved: 0, message: '' };
render() {
- return <div className="fieldLoader">{`Requested: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>;
+ return <div className="fieldLoader">{`${FieldLoader.ServerLoadStatus.message} request: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>;
}
}
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 183d644d3..f3fcc87f7 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -1,4 +1,4 @@
-import { action, observable } from 'mobx';
+import { action, computed, observable } from 'mobx';
import { alias, list, serializable } from 'serializr';
import { DocServer } from '../client/DocServer';
import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
@@ -72,13 +72,13 @@ class ListImpl<T extends Field> extends ObjectField {
return res;
},
sort(cmpFunc: any) {
- this[Self].__realFields(); // coerce retrieving entire array
+ this[Self].__realFields; // coerce retrieving entire array
const res = this[Self].__fieldTuples.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
this[SelfProxy][FieldChanged]?.();
return res;
},
splice: action(function (this: any, start: number, deleteCount: number, ...items: any[]) {
- this[Self].__realFields(); // coerce retrieving entire array
+ this[Self].__realFields; // coerce retrieving entire array
items = items.map(toObjectField);
const list = this[Self];
const removed = list.__fieldTuples.filter((item: any, i: number) => i >= start && i < start + deleteCount);
@@ -125,104 +125,104 @@ class ListImpl<T extends Field> extends ObjectField {
},
/// Accessor methods
concat: action(function (this: any, ...items: any[]) {
- this[Self].__realFields();
+ this[Self].__realFields;
return this[Self].__fieldTuples.map(toRealField).concat(...items);
}),
includes(valueToFind: any, fromIndex: number) {
if (valueToFind instanceof RefField) {
- return this[Self].__realFields().includes(valueToFind, fromIndex);
+ return this[Self].__realFields.includes(valueToFind, fromIndex);
} else {
return this[Self].__fieldTuples.includes(valueToFind, fromIndex);
}
},
indexOf(valueToFind: any, fromIndex: number) {
if (valueToFind instanceof RefField) {
- return this[Self].__realFields().indexOf(valueToFind, fromIndex);
+ return this[Self].__realFields.indexOf(valueToFind, fromIndex);
}
return this[Self].__fieldTuples.indexOf(valueToFind, fromIndex);
},
join(separator: any) {
- this[Self].__realFields();
+ this[Self].__realFields;
return this[Self].__fieldTuples.map(toRealField).join(separator);
},
lastElement() {
- return this[Self].__realFields().lastElement();
+ return this[Self].__realFields.lastElement();
},
lastIndexOf(valueToFind: any, fromIndex: number) {
if (valueToFind instanceof RefField) {
- return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex);
+ return this[Self].__realFields.lastIndexOf(valueToFind, fromIndex);
} else {
return this[Self].__fieldTuples.lastIndexOf(valueToFind, fromIndex);
}
},
slice(begin: number, end: number) {
- this[Self].__realFields();
+ this[Self].__realFields;
return this[Self].__fieldTuples.slice(begin, end).map(toRealField);
},
/// Iteration methods
entries() {
- return this[Self].__realFields().entries();
+ return this[Self].__realFields.entries();
},
every(callback: any, thisArg: any) {
- return this[Self].__realFields().every(callback, thisArg);
+ return this[Self].__realFields.every(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
filter(callback: any, thisArg: any) {
- return this[Self].__realFields().filter(callback, thisArg);
+ return this[Self].__realFields.filter(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
find(callback: any, thisArg: any) {
- return this[Self].__realFields().find(callback, thisArg);
+ return this[Self].__realFields.find(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
findIndex(callback: any, thisArg: any) {
- return this[Self].__realFields().findIndex(callback, thisArg);
+ return this[Self].__realFields.findIndex(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
forEach(callback: any, thisArg: any) {
- return this[Self].__realFields().forEach(callback, thisArg);
+ return this[Self].__realFields.forEach(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
map(callback: any, thisArg: any) {
- return this[Self].__realFields().map(callback, thisArg);
+ return this[Self].__realFields.map(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
reduce(callback: any, initialValue: any) {
- return this[Self].__realFields().reduce(callback, initialValue);
+ return this[Self].__realFields.reduce(callback, initialValue);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
},
reduceRight(callback: any, initialValue: any) {
- return this[Self].__realFields().reduceRight(callback, initialValue);
+ return this[Self].__realFields.reduceRight(callback, initialValue);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
},
some(callback: any, thisArg: any) {
- return this[Self].__realFields().some(callback, thisArg);
+ return this[Self].__realFields.some(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
values() {
- return this[Self].__realFields().values();
+ return this[Self].__realFields.values();
},
[Symbol.iterator]() {
- return this[Self].__realFields().values();
+ return this[Self].__realFields.values();
},
};
static listGetter(target: any, prop: string | symbol, receiver: any): any {
@@ -262,7 +262,7 @@ class ListImpl<T extends Field> extends ObjectField {
// this requests all ProxyFields at the same time to avoid the overhead
// of separate network requests and separate updates to the React dom.
- private __realFields() {
+ @computed private get __realFields() {
const unrequested = this[FieldTuples].filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<RefField>);
// if we find any ProxyFields that don't have a current value, then
// start the server request for all of them
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 251b1149d..69dbe9756 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -1,11 +1,10 @@
-import { Field, Opt, FieldResult, Doc } from './Doc';
+import { DateField } from './DateField';
+import { Doc, Field, FieldResult, Opt } from './Doc';
import { List } from './List';
import { RefField } from './RefField';
-import { DateField } from './DateField';
-import { ScriptField } from './ScriptField';
-import { URLField, WebField, ImageField, CsvField } from './URLField';
-import { TextField } from '@material-ui/core';
import { RichTextField } from './RichTextField';
+import { ScriptField } from './ScriptField';
+import { CsvField, ImageField, WebField } from './URLField';
export type ToType<T extends InterfaceValue> = T extends 'string'
? string
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index 8db4d6003..8ac20b1e5 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -25,7 +25,7 @@ export abstract class URLField extends ObjectField {
constructor(url: URL | string) {
super();
if (typeof url === 'string') {
- url = (url.startsWith('http') || url.startsWith('https')) ? new URL(url) : new URL(url, window.location.origin);
+ url = url.startsWith('http') ? new URL(url) : new URL(url, window.location.origin);
}
this.url = url;
}
diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx
index 6cbf86f77..dc3a73def 100644
--- a/src/mobile/MobileMain.tsx
+++ b/src/mobile/MobileMain.tsx
@@ -12,7 +12,7 @@ AssignAllExtensions();
const info = await CurrentUserUtils.loadCurrentUser();
DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + ' (mobile)');
await Docs.Prototypes.initialize();
- await CurrentUserUtils.loadUserDocument(info.id);
+ await CurrentUserUtils.loadUserDocument(info);
document.getElementById('root')!.addEventListener(
'wheel',
event => {
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 820e815d8..ebc9deab7 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -12,6 +12,7 @@ import { AcceptableMedia, Upload } from '../SharedMediaTypes';
import ApiManager, { Registration } from './ApiManager';
import { SolrManager } from './SearchManager';
import v4 = require('uuid/v4');
+import { DashVersion } from '../../fields/DocSymbols';
const AdmZip = require('adm-zip');
const imageDataUri = require('image-data-uri');
const fs = require('fs');
@@ -45,7 +46,7 @@ export default class UploadManager extends ApiManager {
method: Method.POST,
subscription: '/ping',
secureHandler: async ({ req, res }) => {
- _success(res, { message: 'pong', date: new Date() });
+ _success(res, { message: DashVersion, date: new Date() });
},
});
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index c3dadd821..8b7994eac 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -5,7 +5,8 @@ import { msToTime } from '../ActionUtilities';
import * as bcrypt from 'bcrypt-nodejs';
import { Opt } from '../../fields/Doc';
import { WebSocket } from '../websocket';
-import { DashStats } from '../DashStats';
+import { resolvedPorts } from '../server_Initialization';
+import { DashVersion } from '../../fields/DocSymbols';
export const timeMap: { [id: string]: number } = {};
interface ActivityUnit {
@@ -68,7 +69,18 @@ export default class UserManager extends ApiManager {
register({
method: Method.GET,
subscription: '/getCurrentUser',
- secureHandler: ({ res, user: { _id, email, cacheDocumentIds } }) => res.send(JSON.stringify({ id: _id, email, cacheDocumentIds })),
+ secureHandler: ({ res, user }) =>
+ res.send(
+ JSON.stringify({
+ version: DashVersion,
+ userDocumentId: user.userDocumentId,
+ linkDatabaseId: user.linkDatabaseId,
+ sharingDocumentId: user.sharingDocumentId,
+ email: user.email,
+ cacheDocumentIds: user.cacheDocumentIds,
+ resolvedPorts,
+ })
+ ),
publicHandler: ({ res }) => res.send(JSON.stringify({ id: '__guest__', email: 'guest' })),
});
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index bff60568b..337bb812f 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -44,7 +44,7 @@ function isLocal() {
return /Dash-Web[0-9]*[\\\/]src[\\\/]server[\\\/]public[\\\/](.*)/;
}
-function usingAzure(){
+function usingAzure() {
return process.env.USE_AZURE === 'true';
}
@@ -143,7 +143,6 @@ export namespace DashUploadUtils {
export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> {
return new Promise<Upload.FileResponse<Upload.FileInformation>>((res, rej) => {
- console.log('Uploading YouTube video: ' + videoId);
const name = videoId;
const path = name.replace(/^-/, '__') + '.mp4';
const finalPath = serverPathToFile(Directory.videos, path);
@@ -194,7 +193,7 @@ export namespace DashUploadUtils {
const isAzureOn = usingAzure();
const { type, path, name } = file;
const types = type?.split('/') ?? [];
- uploadProgress.set(overwriteGuid ?? name, 'uploading'); // If the client sent a guid it uses to track upload progress, use that guid. Otherwise, use the file's name.
+ uploadProgress.set(overwriteGuid ?? name, 'uploading'); // If the client sent a guid it uses to track upload progress, use that guid. Otherwise, use the file's name.
const category = types[0];
let format = `.${types[1]}`;
@@ -490,13 +489,13 @@ export namespace DashUploadUtils {
/**
* UploadInspectedImage() takes an image with its metadata. If Azure is being used, this method will call the Azure function
- * to execute the resizing. If Azure is not used, the function will begin to resize the image.
- *
+ * to execute the resizing. If Azure is not used, the function will begin to resize the image.
+ *
* @param metadata metadata object from InspectImage()
* @param filename the name of the file
* @param prefix the prefix to use, which will be set to '' if none is provided.
* @param cleanUp a boolean indicating if the files should be deleted after upload. True by default.
- * @returns the accessPaths for the resized files.
+ * @returns the accessPaths for the resized files.
*/
export const UploadInspectedImage = async (metadata: Upload.InspectionResults, filename?: string, prefix = '', cleanUp = true): Promise<Upload.ImageInformation> => {
const { requestable, source, ...remaining } = metadata;
@@ -504,23 +503,26 @@ export namespace DashUploadUtils {
const { images } = Directory;
const information: Upload.ImageInformation = {
accessPaths: {
- agnostic: usingAzure() ? {
- client: BLOBSTORE_URL + `/${filename}`,
- server: BLOBSTORE_URL + `/${filename}`
- } : getAccessPaths(images, resolved)
+ agnostic: usingAzure()
+ ? {
+ client: BLOBSTORE_URL + `/${resolved}`,
+ server: BLOBSTORE_URL + `/${resolved}`,
+ }
+ : getAccessPaths(images, resolved),
},
...metadata,
};
- let writtenFiles: { [suffix: string] : string};
+ let writtenFiles: { [suffix: string]: string };
if (usingAzure()) {
if (!RESIZE_FUNCTION_URL) {
- throw new Error("Resize function URL not provided.");
+ throw new Error('Resize function URL not provided.');
}
try {
const response = await axios.post(RESIZE_FUNCTION_URL, {
- url: requestable
+ url: requestable,
+ filename: resolved,
});
writtenFiles = response.data.writtenFiles;
} catch (err) {
@@ -576,8 +578,8 @@ export namespace DashUploadUtils {
/**
* outputResizedImages takes in a readable stream and resizes the images according to the sizes defined at the top of this file.
- *
- * The new images will be saved to the server with the corresponding prefixes.
+ *
+ * The new images will be saved to the server with the corresponding prefixes.
* @param streamProvider a Stream of the image to process, taken from the /parsed_files location
* @param outputFileName the basename (No suffix) of the outputted file.
* @param outputDirectory the directory to output to, usually Directory.Images
@@ -608,7 +610,7 @@ export namespace DashUploadUtils {
return [
{ suffix: SizeSuffix.Original },
...Object.values(DashUploadUtils.Sizes).map(({ suffix, width }) => {
- let initial: sharp.Sharp | undefined = sharp().resize(width, undefined, { withoutEnlargement: true });
+ let initial: sharp.Sharp | undefined = sharp({ failOnError: false }).resize(width, undefined, { withoutEnlargement: true });
if (pngs.includes(ext)) {
initial = initial.png(pngOptions);
} else if (jpgs.includes(ext)) {
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index c1934451c..354f809e0 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -100,7 +100,7 @@ function buildWithMiddleware(server: express.Express) {
passport.session(),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
res.locals.user = req.user;
- if (req.originalUrl.endsWith('.png') /*|| req.originalUrl.endsWith(".js")*/ && req.method === 'GET' && (res as any)._contentLength) {
+ if ((req.originalUrl.endsWith('.png') || req.originalUrl.endsWith('.jpg') || (process.env.RELEASE === 'true' && req.originalUrl.endsWith('.js'))) && req.method === 'GET') {
const period = 30000;
res.set('Cache-control', `public, max-age=${period}`);
} else {
@@ -176,6 +176,8 @@ function proxyServe(req: any, requrl: string, response: any) {
const htmlText = htmlInputText
.toString('utf8')
.replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
+ .replace('<script', '<noscript')
+ .replace('</script', '</noscript')
// .replace(/href="https?([^"]*)"/g, httpsToCors)
.replace(/data-srcset="[^"]*"/g, '')
.replace(/srcset="[^"]*"/g, '')