diff options
-rw-r--r-- | package-lock.json | 73 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/DocumentDecorations.scss | 1 | ||||
-rw-r--r-- | src/Main.tsx | 21 | ||||
-rw-r--r-- | src/documents/Documents.ts | 6 | ||||
-rw-r--r-- | src/fields/Key.ts | 1 | ||||
-rw-r--r-- | src/viewmodels/DocumentViewModel.ts | 10 | ||||
-rw-r--r-- | src/views/ContextMenu.scss | 34 | ||||
-rw-r--r-- | src/views/ContextMenu.tsx | 68 | ||||
-rw-r--r-- | src/views/ContextMenuItem.tsx | 17 | ||||
-rw-r--r-- | src/views/freeformcanvas/CollectionFreeFormView.tsx | 96 | ||||
-rw-r--r-- | src/views/freeformcanvas/FreeFormCanvas.tsx | 6 | ||||
-rw-r--r-- | src/views/nodes/DocumentView.tsx | 48 | ||||
-rw-r--r-- | src/views/nodes/FieldTextBox.scss | 10 | ||||
-rw-r--r-- | src/views/nodes/FieldTextBox.tsx | 5 |
15 files changed, 329 insertions, 68 deletions
diff --git a/package-lock.json b/package-lock.json index 955f0f3ae..7dfe02494 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,16 +4,29 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@fortawesome/fontawesome-common-types": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.12.tgz", + "integrity": "sha512-ISLNpEx6fhJTYYkvBeo/4DHeL5EIA+VybJoOOnH67m6uXt6V6VFizdEN4qchHagNIeZfzI0LnA22gk0wbVPv/g==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.12.tgz", + "integrity": "sha512-cqTfa3vZ+Z9UYQnmLfCOwyLnf0Xcoxkmm/BSaI29Yikzu9zIeD4es7lBZMDqLOXYSEQC+rCr8caxFlGJcJVA+w==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.12" + } + }, "@types/chai": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz", - "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==", + "integrity": "sha1-G44zthqMCcvh+FEzBxuqDb+fpxo=", "dev": true }, "@types/mocha": { "version": "5.2.5", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz", - "integrity": "sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww==", + "integrity": "sha1-ikrM/EA8EkoLr+ip/GGgXsEDIHM=", "dev": true }, "@types/node": { @@ -117,7 +130,7 @@ "@types/react-dom": { "version": "16.0.11", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.11.tgz", - "integrity": "sha512-x6zUx9/42B5Kl2Vl9HlopV8JF64wLpX3c+Pst9kc1HgzrsH+mkehe/zmHMQTplIrR48H2gpU7ZqurQolYu8XBA==", + "integrity": "sha1-vRDMsNkmA0P0uaSdT3qDMKXB8IE=", "dev": true, "requires": { "@types/react": "*" @@ -126,7 +139,7 @@ "@types/uuid": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", - "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==", + "integrity": "sha1-evaTYPpl7w3stB/RUL9MpcDO/fU=", "requires": { "@types/node": "*" } @@ -579,7 +592,7 @@ "awesome-typescript-loader": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz", - "integrity": "sha512-slv66OAJB8orL+UUaTI3pKlLorwIvS4ARZzYR9iJJyGsEgOqueMfOMdKySWzZ73vIkEe3fcwFgsKMg4d8zyb1g==", + "integrity": "sha1-pB2veEdRX0klzbqjB11h8onpE/w=", "dev": true, "requires": { "chalk": "^2.4.1", @@ -982,7 +995,7 @@ "chai": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "integrity": "sha1-dgqnLPION5XoSxKHfODoNzeqKeU=", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -1289,7 +1302,7 @@ "copy-webpack-plugin": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", - "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", + "integrity": "sha1-5/QN2KaEd9QF3Rt6hUquMksVi64=", "dev": true, "requires": { "cacache": "^10.0.4", @@ -1388,7 +1401,7 @@ "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "integrity": "sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=", "dev": true, "requires": { "p-try": "^1.0.0" @@ -1534,7 +1547,7 @@ "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "integrity": "sha1-C3mpMgTXtgDUsoUNH2bCo0lRx3A=", "dev": true, "requires": { "ajv": "^6.1.0", @@ -4165,12 +4178,12 @@ "mobx": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.8.0.tgz", - "integrity": "sha512-NsZB+9bF5j+nv9Qwk6bNeE3np26a4TbTGkMpOLf6o1zXoM9BtHPQn/00px4uZ2AXJXtQML5P4MEWdMm6icMIfQ==" + "integrity": "sha1-z1nq5Lqi/eQ4f/tkYrfM87SXsDw=" }, "mobx-react": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.4.3.tgz", - "integrity": "sha512-WC8yFlwvJ91hy8j6CrydAuFteUafcuvdITFQeHl3LRIf5ayfT/4W3M/byhEYD2BcJWejeXr8y4Rh2H26RunCRQ==", + "integrity": "sha1-Zwm33YlnDEDpgVkUrCyknMAr+0c=", "requires": { "hoist-non-react-statics": "^3.0.0", "react-lifecycles-compat": "^3.0.2" @@ -4179,12 +4192,12 @@ "mobx-react-devtools": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/mobx-react-devtools/-/mobx-react-devtools-6.0.3.tgz", - "integrity": "sha512-PY+lG6XeWaC0DFnDaVC7ImrHJQu7XVXNy4z4fmqHzWc3p+fJisKmaZNwgToO2vJGBghz98Mx2yXUBLw1Ba2mPQ==" + "integrity": "sha1-TVNsjN132fS4H16EFde+3MxqKpk=" }, "mocha": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "integrity": "sha1-bYrlCPWRZ/lA8rWzxKYSrlDJCuY=", "dev": true, "requires": { "browser-stdout": "1.3.1", @@ -4203,13 +4216,13 @@ "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "integrity": "sha1-30boZ9D8Kuxmo0ZitAapzK//Ww8=", "dev": true }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4218,7 +4231,7 @@ "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -4232,7 +4245,7 @@ "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "integrity": "sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -4398,7 +4411,7 @@ "node-sass": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", - "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "integrity": "sha1-GD+uw5jpy+k7pDNi4naMqYimNpo=", "requires": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -5206,7 +5219,7 @@ "react": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/react/-/react-16.7.0.tgz", - "integrity": "sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==", + "integrity": "sha512-ZUj2lkUDLjwJaGu4WD0dYSvsfIyhQt2l/AJDlg4ij+rCDU3fSFKgHWanNovViUoaWHAxgrpft3KGFfvWPZH5LA==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -5217,7 +5230,7 @@ "react-dom": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.7.0.tgz", - "integrity": "sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==", + "integrity": "sha512-GfG8Vh/jMcnJKDpv7T6O1pS/WVqiocjPQ9o9cscW8bjR9W36DT3Xb4pDZT70t1xyVvX48/NrTQGz0H7I3fCLhQ==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -5233,7 +5246,7 @@ "react-jsx-parser": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-1.13.0.tgz", - "integrity": "sha512-oypIhM30ESZ8UkU0xDmzSV2Mtb2mVvtVnyNzjDxx2h2PCHpYFdDVLx1c15E3ot6nTIVlIh072tWwS3iJ7VVgmg==", + "integrity": "sha1-Dstuus1O41S68+h85SXcJuiPJtQ=", "requires": { "acorn-jsx": "^4.1.1", "react": "^16.4.0" @@ -5618,7 +5631,7 @@ "sass-loader": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", - "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", + "integrity": "sha1-Fv1ROMuLQkv4p1lSihly1yqtBp0=", "dev": true, "requires": { "clone-deep": "^2.0.1", @@ -6321,7 +6334,7 @@ "style-loader": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", - "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "integrity": "sha1-y5FUYG8+dxq2xKtjcCahBJF02SU=", "dev": true, "requires": { "loader-utils": "^1.1.0", @@ -6331,7 +6344,7 @@ "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "integrity": "sha1-C3mpMgTXtgDUsoUNH2bCo0lRx3A=", "dev": true, "requires": { "ajv": "^6.1.0", @@ -6525,7 +6538,7 @@ "ts-node": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "integrity": "sha1-lWLcLR5tJI0kvFX3c+P2FDN9m68=", "dev": true, "requires": { "arrify": "^1.0.0", @@ -6775,7 +6788,7 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" }, "v8-compile-cache": { "version": "2.0.2", @@ -6925,7 +6938,7 @@ "webpack-dev-server": { "version": "3.1.14", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", - "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", + "integrity": "sha1-YPsim5l/xaCh/GI3QhAwGAlZ1Gk=", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -6963,7 +6976,7 @@ "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", "dev": true, "requires": { "ms": "^2.1.1" @@ -6978,7 +6991,7 @@ "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "integrity": "sha1-C3mpMgTXtgDUsoUNH2bCo0lRx3A=", "dev": true, "requires": { "ajv": "^6.1.0", @@ -6989,7 +7002,7 @@ "webpack-log": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "integrity": "sha1-W3ko4GN1k/EZ0y9iJ8HgrDHhtH8=", "dev": true, "requires": { "ansi-colors": "^3.0.0", diff --git a/package.json b/package.json index 08e999058..71ca3cc8b 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "webpack-dev-server": "^3.1.14" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.12", "@types/prosemirror-commands": "^1.0.1", "@types/prosemirror-history": "^1.0.1", "@types/prosemirror-keymap": "^1.0.1", diff --git a/src/DocumentDecorations.scss b/src/DocumentDecorations.scss index fff4d201a..5a9ff7d01 100644 --- a/src/DocumentDecorations.scss +++ b/src/DocumentDecorations.scss @@ -1,6 +1,5 @@ #documentDecorations-container { position: absolute; - z-index: 1000; display: grid; grid-template-rows: 20px 1fr 20px; grid-template-columns: 20px 1fr 20px; diff --git a/src/Main.tsx b/src/Main.tsx index 0a683c858..cb91c33a3 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -9,7 +9,7 @@ import { FreeFormCanvas } from './views/freeformcanvas/FreeFormCanvas'; import { Key, KeyStore as KS, KeyStore } from './fields/Key'; import { NumberField } from './fields/NumberField'; import { Document } from './fields/Document'; -import { configure, runInAction } from 'mobx'; +import { configure, runInAction, action } from 'mobx'; import { NodeStore } from './stores/NodeStore'; import { Documents } from './documents/Documents'; import { DocumentDecorations } from './DocumentDecorations'; @@ -17,6 +17,7 @@ import { CollectionFreeFormView } from './views/freeformcanvas/CollectionFreeFor import { ListField } from './fields/ListField'; import { DocumentView } from './views/nodes/DocumentView'; import { DocumentViewModel } from './viewmodels/DocumentViewModel'; +import { ContextMenu } from './views/ContextMenu'; configure({ enforceActions: "observed" @@ -26,11 +27,27 @@ const mainNodeCollection = new Array<Document>(); let mainContainer = Documents.CollectionDocument(mainNodeCollection, { x: 0, y: 0, width: window.screen.width, height: window.screen.height }) +let mainContvm = new DocumentViewModel(mainContainer); +mainContvm.IsMainDoc = true; + +window.addEventListener("drop", function(e) { + e.preventDefault(); +}, false) +window.addEventListener("dragover", function(e) { + e.preventDefault(); +}, false) +document.addEventListener("pointerdown", action(function(e: PointerEvent) { + if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) { + ContextMenu.Instance.clearItems() + } +}), true) + ReactDOM.render(( <div style={{display: "grid", width: "100vw", height: "100vh"}}> <h1>Dash Web</h1> + <DocumentView dvm={mainContvm} parent={undefined} /> <DocumentDecorations /> - <DocumentView dvm={new DocumentViewModel(mainContainer)} /> + <ContextMenu /> </div>), document.getElementById('root')); diff --git a/src/documents/Documents.ts b/src/documents/Documents.ts index cc08f4123..75cc3b491 100644 --- a/src/documents/Documents.ts +++ b/src/documents/Documents.ts @@ -35,7 +35,7 @@ export namespace Documents { textProto.SetField(KeyStore.Y, new NumberField(0)); textProto.SetField(KeyStore.Width, new NumberField(300)); textProto.SetField(KeyStore.Height, new NumberField(150)); - textProto.SetField(KeyStore.Layout, new TextField("<FieldTextBox doc={doc} fieldKey={DataKey} />")); + textProto.SetField(KeyStore.Layout, new TextField("<FieldTextBox doc={dvm.Doc} fieldKey={DataKey} />")); textProto.SetField(KeyStore.LayoutKeys, new ListField([KeyStore.Data])); } return textProto; @@ -73,11 +73,11 @@ export namespace Documents { function GetCollectionPrototype(): Document { if(!collectionProto) { collectionProto = new Document(); - collectionProto.SetField(KeyStore.X, new NumberField(150)); + collectionProto.SetField(KeyStore.X, new NumberField(0)); collectionProto.SetField(KeyStore.Y, new NumberField(0)); collectionProto.SetField(KeyStore.Width, new NumberField(300)); collectionProto.SetField(KeyStore.Height, new NumberField(300)); - collectionProto.SetField(KeyStore.Layout, new TextField('<CollectionFreeFormView doc={doc} fieldKey={DataKey} isSelected={isSelected}/>')); + collectionProto.SetField(KeyStore.Layout, new TextField('<CollectionFreeFormView dvm={dvm} fieldKey={DataKey} isSelected={isSelected}/>')); collectionProto.SetField(KeyStore.LayoutKeys, new ListField([KeyStore.Data])); } return collectionProto; diff --git a/src/fields/Key.ts b/src/fields/Key.ts index db30f545d..1f8ba0be5 100644 --- a/src/fields/Key.ts +++ b/src/fields/Key.ts @@ -36,6 +36,7 @@ export namespace KeyStore { export let Y = new Key("Y"); export let PanX = new Key("PanX"); export let PanY = new Key("PanY"); + export let Scale = new Key("Scale"); export let Width = new Key("Width"); export let Height = new Key("Height"); export let Data = new Key("Data"); diff --git a/src/viewmodels/DocumentViewModel.ts b/src/viewmodels/DocumentViewModel.ts index f3154db54..21e88f964 100644 --- a/src/viewmodels/DocumentViewModel.ts +++ b/src/viewmodels/DocumentViewModel.ts @@ -9,13 +9,13 @@ export class DocumentViewModel { return this.doc; } - private _selected = false; + private _isMainDoc = false - get Selected() : boolean { - return this._selected; + get IsMainDoc(): boolean { + return this._isMainDoc; } - set Selected(isSelected: boolean) { - this._selected = isSelected; + set IsMainDoc(v: boolean) { + this._isMainDoc = v; } }
\ No newline at end of file diff --git a/src/views/ContextMenu.scss b/src/views/ContextMenu.scss new file mode 100644 index 000000000..234f82eb9 --- /dev/null +++ b/src/views/ContextMenu.scss @@ -0,0 +1,34 @@ +.contextMenu-cont { + position: absolute; + display: flex; + z-index: 1000; + box-shadow: #AAAAAA .2vw .2vw .4vw; +} + +.contextMenu-item { + width: 10vw; + height: 4vh; + background: #DDDDDD; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; +} + +.contextMenu-item:hover { + transition: all .1s; + background: #AAAAAA +} + +.contextMenu-description { + font-size: 1.5vw; + text-align: left; + width: 8vw; +}
\ No newline at end of file diff --git a/src/views/ContextMenu.tsx b/src/views/ContextMenu.tsx new file mode 100644 index 000000000..299ce91c6 --- /dev/null +++ b/src/views/ContextMenu.tsx @@ -0,0 +1,68 @@ +import React = require("react"); +import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; +import { observable } from "mobx"; +import { observer } from "mobx-react"; +import "./ContextMenu.scss" + +@observer +export class ContextMenu extends React.Component { + static Instance: ContextMenu + + @observable private _items: Array<ContextMenuProps> = [{description:"test", event:(e:React.MouseEvent) => e.preventDefault()}]; + @observable private _pageX: number = 0; + @observable private _pageY: number = 0; + @observable private _display: string = "none"; + + private ref: React.RefObject<HTMLDivElement>; + + constructor(props: Readonly<{}>) { + super(props); + + this.ref = React.createRef() + + ContextMenu.Instance = this; + } + + clearItems() { + this._items = [] + this._display = "none" + } + + addItem(item: ContextMenuProps) { + if (this._items.indexOf(item) === -1) { + this._items.push(item); + } + } + + getItems() { + return this._items; + } + + displayMenu(x: number, y: number) { + this._pageX = x + this._pageY = y + + this._display = "flex" + } + + intersects = (x: number, y: number): boolean => { + if (this.ref.current && this._display !== "none") { + if (x >= this._pageX && x <= this._pageX + this.ref.current.getBoundingClientRect().width) { + if (y >= this._pageY && y <= this._pageY + this.ref.current.getBoundingClientRect().height) { + return true; + } + } + } + return false; + } + + render() { + return( + <div className="contextMenu-cont" style={{left: this._pageX, top: this._pageY, display: this._display }} ref={this.ref}> + {this._items.map(prop => { + return <ContextMenuItem {...prop} key={prop.description}/> + })} + </div> + ) + } +}
\ No newline at end of file diff --git a/src/views/ContextMenuItem.tsx b/src/views/ContextMenuItem.tsx new file mode 100644 index 000000000..beacb44d2 --- /dev/null +++ b/src/views/ContextMenuItem.tsx @@ -0,0 +1,17 @@ +import React = require("react"); +import { ContextMenu } from "./ContextMenu"; + +export interface ContextMenuProps { + description: string; + event: (e: React.MouseEvent<HTMLDivElement>) => void; +} + +export class ContextMenuItem extends React.Component<ContextMenuProps> { + render() { + return( + <div className="contextMenu-item" onClick={this.props.event}> + <div className="contextMenu-description">{this.props.description}</div> + </div> + ) + } +}
\ No newline at end of file diff --git a/src/views/freeformcanvas/CollectionFreeFormView.tsx b/src/views/freeformcanvas/CollectionFreeFormView.tsx index 4c8fcec10..84b30ac38 100644 --- a/src/views/freeformcanvas/CollectionFreeFormView.tsx +++ b/src/views/freeformcanvas/CollectionFreeFormView.tsx @@ -2,7 +2,7 @@ import { observer } from "mobx-react"; import { Key, KeyStore } from "../../fields/Key"; import "./FreeFormCanvas.scss"; import React = require("react"); -import { action } from "mobx"; +import { action, observable } from "mobx"; import { Document } from "../../fields/Document"; import { DocumentViewModel } from "../../viewmodels/DocumentViewModel"; import { DocumentView } from "../nodes/DocumentView"; @@ -11,10 +11,12 @@ import { NumberField } from "../../fields/NumberField"; import { SSL_OP_SINGLE_DH_USE } from "constants"; import { DocumentDecorations } from "../../DocumentDecorations"; import { SelectionManager } from "../../util/SelectionManager"; +import { Documents } from "../../documents/Documents"; +import { ContextMenu } from "../ContextMenu"; interface IProps { fieldKey: Key; - doc: Document; + dvm: DocumentViewModel; isSelected: boolean; } @@ -29,10 +31,14 @@ export class CollectionFreeFormView extends React.Component<IProps> { @action onPointerDown = (e: React.PointerEvent): void => { - if (!this.props.isSelected) { + if (!this.props.isSelected && !this.props.dvm.IsMainDoc) { return; } + if (this.props.dvm.IsMainDoc) { + SelectionManager.DeselectAll() + } + e.stopPropagation(); if (e.button === 2) { this._isPointerDown = true; @@ -60,14 +66,14 @@ export class CollectionFreeFormView extends React.Component<IProps> { if (!this._isPointerDown) { return; } - const { doc } = this.props; + const { dvm } = this.props; + const doc = dvm.Doc; let x = doc.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); let y = doc.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); doc.SetFieldValue(KeyStore.PanX, x + e.movementX, NumberField); doc.SetFieldValue(KeyStore.PanY, y + e.movementY, NumberField); - DocumentDecorations.Instance.forceUpdate() } @@ -76,16 +82,75 @@ export class CollectionFreeFormView extends React.Component<IProps> { e.stopPropagation(); let scaleAmount = 1 - (e.deltaY / 1000); - //this.props.store.Scale *= scaleAmount; + let currScale = this.props.dvm.Doc.GetFieldValue(KeyStore.Scale, NumberField, Number(1)); + this.props.dvm.Doc.SetField(KeyStore.Scale, new NumberField(currScale * scaleAmount)); + + const panx: number = this.props.dvm.Doc.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); + const pany: number = this.props.dvm.Doc.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); + let dx = (e.pageX - window.screen.width / 2) * currScale * (scaleAmount - 1) + let dy = (e.pageY - window.screen.height / 2) * currScale * (scaleAmount - 1) + + this.props.dvm.Doc.SetFieldValue(KeyStore.PanX, panx - dx, NumberField); + this.props.dvm.Doc.SetFieldValue(KeyStore.PanY, pany - dy, NumberField); + + DocumentDecorations.Instance.forceUpdate() + } + + onDrop = (e: React.DragEvent): void => { + e.stopPropagation() + e.preventDefault() + let fReader = new FileReader() + let file = e.dataTransfer.items[0].getAsFile(); + let that = this; + const panx: number = this.props.dvm.Doc.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); + const pany: number = this.props.dvm.Doc.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); + let x = e.pageX - panx + let y = e.pageY - pany + + fReader.addEventListener("load", action("drop", (event) => { + if (fReader.result) { + let url = "" + fReader.result; + let doc = Documents.ImageDocument(url, { + x: x, y: y + }) + let docs = that.props.dvm.Doc.GetFieldT(KeyStore.Data, ListField); + if (!docs) { + docs = new ListField<Document>(); + that.props.dvm.Doc.SetField(KeyStore.Data, docs) + } + docs.Data.push(doc); + } + }), false) + + if (file) { + fReader.readAsDataURL(file) + } + } + + @action + removeDocument = (doc: Document): void => { + const value: Document[] = this.props.dvm.Doc.GetFieldValue(this.props.fieldKey, ListField, []) + if (value.indexOf(doc) !== -1) { + value.splice(value.indexOf(doc), 1) + + SelectionManager.DeselectAll() + ContextMenu.Instance.clearItems() + } + } + + onDragOver = (e: React.DragEvent): void => { + // console.log(e.dataTransfer) } render() { - const { fieldKey, doc } = this.props; - const value: Document[] = doc.GetFieldValue(fieldKey, ListField, []); - const panx: number = doc.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); - const pany: number = doc.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); + const { fieldKey, dvm } = this.props; + + const value: Document[] = dvm.Doc.GetFieldValue(fieldKey, ListField, []); + const panx: number = dvm.Doc.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); + const pany: number = dvm.Doc.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); + const currScale: number = dvm.Doc.GetFieldValue(KeyStore.Scale, NumberField, Number(1)); + // DocumentDecorations.Instance.forceUpdate() return ( - <div className="border" style={{ borderStyle: "solid", borderWidth: "2px" @@ -94,11 +159,12 @@ export class CollectionFreeFormView extends React.Component<IProps> { width: "100%", height: "calc(100% - 4px)", overflow: "hidden" - }}> - <div className="collectionfreeformview" style={{ transform: `translate(${panx}px, ${pany}px)`, transformOrigin: '50% 50%', width: "100%", height: "100%" }}> - <div className="node-container" style={{width: "100%", height: "100%"}}> + }} onDrop={this.onDrop} onDragOver={this.onDragOver}> + <div className="collectionfreeformview" style={{ transform: `translate(${panx}px, ${pany}px) scale(${currScale}, ${currScale})`, transformOrigin: `50%, 50%`}}> + + <div className="node-container"> {value.map(doc => { - return (<DocumentView key={doc.Id} dvm={new DocumentViewModel(doc)} />); + return (<DocumentView key={doc.Id} parent={this} dvm={new DocumentViewModel(doc)} />); })} </div> </div> diff --git a/src/views/freeformcanvas/FreeFormCanvas.tsx b/src/views/freeformcanvas/FreeFormCanvas.tsx index de5e88fa1..d7dc0ecaa 100644 --- a/src/views/freeformcanvas/FreeFormCanvas.tsx +++ b/src/views/freeformcanvas/FreeFormCanvas.tsx @@ -79,9 +79,9 @@ export class FreeFormCanvas extends React.Component<IProps> { <div className="freeformcanvas-container" onPointerDown={this.onPointerDown} onWheel={this.onPointerWheel} onContextMenu={(e) => e.preventDefault()}> <div className="freeformcanvas" style={{ transform: store.Transform, transformOrigin: '50% 50%' }}> <div className="node-container"> - {this.props.store.Docs.map(doc => { - return (<DocumentView key={doc.Id} dvm={new DocumentViewModel(doc)} />); - })} + {/* {this.props.store.Docs.map(doc => { + return (<DocumentView key={doc.Id} parent={null} dvm={new DocumentViewModel(doc)} />); + })} */} </div> </div> </div> diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index e37172943..ee6269430 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -1,6 +1,6 @@ import { observer } from "mobx-react"; import React = require("react"); -import { computed, observable } from "mobx"; +import { computed, observable, action } from "mobx"; import { KeyStore, Key } from "../../fields/Key"; import { NumberField } from "../../fields/NumberField"; import { TextField } from "../../fields/TextField"; @@ -12,15 +12,19 @@ import { CollectionFreeFormView } from "../freeformcanvas/CollectionFreeFormView import "./NodeView.scss" import { SelectionManager } from "../../util/SelectionManager"; import { DocumentDecorations } from "../../DocumentDecorations"; +import { ContextMenu } from "../ContextMenu"; +import { Opt } from "../../fields/Field"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? interface IProps { dvm: DocumentViewModel; + parent: Opt<CollectionFreeFormView>; } @observer export class DocumentView extends React.Component<IProps> { private _mainCont = React.createRef<HTMLDivElement>(); + private _contextMenuCanOpen = false; get mainCont(): React.RefObject<HTMLDivElement> { return this._mainCont @@ -91,8 +95,10 @@ export class DocumentView extends React.Component<IProps> { onPointerDown = (e: React.PointerEvent): void => { e.stopPropagation(); + if (e.button === 2) { this._isPointerDown = true; + this._contextMenuCanOpen = true; document.removeEventListener("pointermove", this.onPointerMove); document.addEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -120,6 +126,7 @@ export class DocumentView extends React.Component<IProps> { if (!this._isPointerDown) { return; } + this._contextMenuCanOpen = false this.x += e.movementX; this.y += e.movementY; DocumentDecorations.Instance.opacity = 0 @@ -132,10 +139,39 @@ export class DocumentView extends React.Component<IProps> { } } + onClick = (e: React.MouseEvent): void => { } + + deleteClicked = (e: React.MouseEvent): void => { + if (this.props.parent) { + this.props.parent.removeDocument(this.props.dvm.Doc) + } + } + + @action + onContextMenu = (e: React.MouseEvent): void => { + e.preventDefault() + + if (!this._contextMenuCanOpen) { + return; + } + + if (this.props.dvm.IsMainDoc) { + ContextMenu.Instance.clearItems() + } + else { + // DocumentViews should stop propogation of this event + e.stopPropagation(); + + ContextMenu.Instance.clearItems(); + ContextMenu.Instance.addItem({description: "Delete", event: this.deleteClicked}) + ContextMenu.Instance.displayMenu(e.pageX, e.pageY) + } + } + render() { let doc = this.props.dvm.Doc; let bindings: any = { - doc: doc, + dvm: this.props.dvm, isSelected: this.selected }; for (const key of this.layoutKeys) { @@ -154,11 +190,9 @@ export class DocumentView extends React.Component<IProps> { width: this.width, height: this.height, }} - onContextMenu={ - (e) => { - e.preventDefault() - }} - onPointerDown={this.onPointerDown}> + onContextMenu={this.onContextMenu} + onPointerDown={this.onPointerDown} + onClick={this.onClick}> <JsxParser components={{ FieldTextBox, FreeFormCanvas, CollectionFreeFormView }} bindings={bindings} diff --git a/src/views/nodes/FieldTextBox.scss b/src/views/nodes/FieldTextBox.scss new file mode 100644 index 000000000..5c95fe2b2 --- /dev/null +++ b/src/views/nodes/FieldTextBox.scss @@ -0,0 +1,10 @@ +.ProseMirror { + margin-top: -1em; + width: 100%; + height: 100%; +} + +.fieldTextBox-cont { + background: white; + padding: 1vw; +}
\ No newline at end of file diff --git a/src/views/nodes/FieldTextBox.tsx b/src/views/nodes/FieldTextBox.tsx index dbac3906a..3b8743627 100644 --- a/src/views/nodes/FieldTextBox.tsx +++ b/src/views/nodes/FieldTextBox.tsx @@ -13,6 +13,8 @@ import {baseKeymap} from "prosemirror-commands" import {undo, redo, history} from "prosemirror-history" import { Opt } from "../../fields/Field"; +import "./FieldTextBox.scss" + interface IProps { fieldKey:Key; doc:Document; @@ -34,7 +36,6 @@ interface IProps { // specified Key and assigns it to an HTML input node. When changes are made tot his node, // this will edit the document and assign the new value to that field. // -@observer export class FieldTextBox extends React.Component<IProps> { private _ref: React.RefObject<HTMLDivElement>; private _editorView: Opt<EditorView>; @@ -112,6 +113,6 @@ export class FieldTextBox extends React.Component<IProps> { } render() { - return (<div ref={this._ref} />) + return (<div className="fieldTextBox-cont" ref={this._ref} />) } }
\ No newline at end of file |