Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
307a13f7be | ||
|
|
4e96d23585 | ||
|
|
2fd4b67a08 | ||
|
|
50e266e3e4 | ||
|
|
7ca69b809a | ||
|
|
80c7c01805 | ||
|
|
fc89bd9ce1 | ||
|
|
2cfd36147a | ||
|
|
0e13a8b033 | ||
|
|
680bd58442 | ||
|
|
95db9f6528 | ||
|
|
6205d08451 | ||
|
|
14786b2144 | ||
|
|
e25155556f | ||
|
|
016837f466 | ||
|
|
fff03c05bf | ||
|
|
5dbb11bac8 | ||
|
|
d80870e835 | ||
|
|
a98e381f8c | ||
|
|
72aec29e27 | ||
|
|
2eb18acfef | ||
|
|
e1a1b31213 | ||
|
|
5febc490b4 | ||
|
|
48541347f0 | ||
|
|
cc79ea5f7e | ||
|
|
46f6cb2da9 | ||
|
|
34c3d07112 | ||
|
|
d85dce8d02 | ||
|
|
89ed2d602f | ||
|
|
94dd3dbf0e | ||
|
|
c593a134b3 | ||
|
|
dc638ea8fc | ||
|
|
759c8a0030 | ||
|
|
aea80c6586 | ||
|
|
46e2efa576 | ||
|
|
c508e15546 | ||
|
|
d61effc615 | ||
|
|
12be6dc69f | ||
|
|
17e3d16436 | ||
|
|
233479a1bc | ||
|
|
57e5246022 | ||
|
|
dda22e0de6 | ||
|
|
618955e1ee | ||
|
|
8c7d4faeca | ||
|
|
c5754c9160 | ||
|
|
3fb3668fc3 | ||
|
|
fa282d6169 | ||
|
|
0ee55c26eb | ||
|
|
3a31b86d48 | ||
|
|
2f06952e4a | ||
|
|
44a40aef96 | ||
|
|
0a69765460 | ||
|
|
2ebd57b29a | ||
|
|
5225d26870 |
6
.github/workflows/build-noodl-editor.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Build noodl-editor
|
name: Build fluxscape-editor
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
@@ -72,6 +72,6 @@ jobs:
|
|||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: noodl-editor-${{ matrix.platform }}-${{ github.head_ref }}-${{ github.sha }}
|
name: fluxscape-editor-${{ matrix.platform }}-${{ github.head_ref }}-${{ github.sha }}
|
||||||
path: publish
|
path: publish
|
||||||
retention-days: "12"
|
retention-days: '12'
|
||||||
|
|||||||
2
.github/workflows/test-noodl-editor.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Test noodl-editor
|
name: Test fluxscape-editor
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
|
|||||||
32
README.md
@@ -1,21 +1,21 @@
|
|||||||
# Noodl
|
# Fluxscape
|
||||||
|
|
||||||
[Noodl](https://noodl.net) is a low-code platform where designers and developers build custom applications and experiences. Designed as a visual programming environment, it aims to expedite your development process. It promotes the swift and efficient creation of applications, requiring minimal coding knowledge.
|
Fluxscape is a low-code platform where designers and developers build custom applications and experiences. Designed as a visual programming environment, it aims to expedite your development process. It promotes the swift and efficient creation of applications, requiring minimal coding knowledge.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
Documentation for how to use Noodl can be found here:
|
Documentation for how to use Fluxscape can be found here:
|
||||||
[https://noodlapp.github.io/noodl-docs/](https://noodlapp.github.io/noodl-docs/)
|
[Fluxscape Documentation](https://docs.fluxscape.io)
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
Main support channel is Discord: [https://www.noodl.net/community](https://www.noodl.net/community)
|
Main support channel is Discord: [Fluxscape Discord](https://discord.gg/fXNW9EXa6A)
|
||||||
|
|
||||||
## Download releases
|
## Download releases
|
||||||
Pre-built binaries can be [downloaded from Github](https://github.com/noodlapp/noodl/releases)
|
Pre-built binaries can be [downloaded from Github](https://github.com/fluxscape/fluxscape/releases)
|
||||||
|
|
||||||
## Note for users who are migrating from the deprecated closed source version
|
## Note for users who are migrating from the deprecated closed source version
|
||||||
- [Migrating the project files and workspaces to a Git provider](https://noodlapp.github.io/noodl-docs/docs/guides/collaboration/migrating-from-noodl-hosted-git)
|
- [Migrating the project files and workspaces to a Git provider](https://docs.fluxscape.io/docs/guides/collaboration/migrating-from-noodl-hosted-git/)
|
||||||
- [Migrate backend and database](https://noodlapp.github.io/noodl-docs/docs/guides/deploy/using-an-external-backend#migrating-from-a-noodl-cloud-service)
|
- [Migrate backend and database](https://docs.fluxscape.io/docs/guides/deploy/using-an-external-backend/#migrating-from-a-noodl-cloud-service)
|
||||||
- [Self-host frontend](https://noodlapp.github.io/noodl-docs/docs/guides/deploy/hosting-frontend)
|
- [Self-host frontend](https://docs.fluxscape.io/docs/guides/deploy/hosting-frontend/)
|
||||||
|
|
||||||
## Building from source
|
## Building from source
|
||||||
|
|
||||||
@@ -23,24 +23,24 @@ Pre-built binaries can be [downloaded from Github](https://github.com/noodlapp/n
|
|||||||
# Install all dependencies
|
# Install all dependencies
|
||||||
$ npm install
|
$ npm install
|
||||||
|
|
||||||
# Start the Noodl Editor and build a production version of the cloud and react runtime (useful when running Noodl from source but want to deploy to production)
|
# Start the Fluxscape Editor and build a production version of the cloud and react runtime (useful when running Fluxscape from source but want to deploy to production)
|
||||||
$ npm start
|
$ npm start
|
||||||
|
|
||||||
# Start the Noodl Editor and watch the filesystem for changes to the runtimes. Development versions of the runtimes, not meant for production (mostly due to source maps and file size)
|
# Start the Fluxscape Editor and watch the filesystem for changes to the runtimes. Development versions of the runtimes, not meant for production (mostly due to source maps and file size)
|
||||||
# This is ideal for a quick workflow when doing changes on the runtimes.
|
# This is ideal for a quick workflow when doing changes on the runtimes.
|
||||||
$ npm run dev
|
$ npm run dev
|
||||||
|
|
||||||
# Start Noodl Editor test runner
|
# Start Fluxscape Editor test runner
|
||||||
$ npm run test:editor
|
$ npm run test:editor
|
||||||
```
|
```
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
This repository contains two different licenses for different parts of the Noodl platform.
|
This repository contains two different licenses for different parts of the Fluxscape platform.
|
||||||
|
|
||||||
- Components related to the editor, used to edit Noodl projects, are under GPLv3
|
- Components related to the editor, used to edit Fluxscape projects, are under GPLv3
|
||||||
- Components related to the end applications, used by the applications Noodl deploys, are under MIT
|
- Components related to the end applications, used by the applications Fluxscape deploys, are under MIT
|
||||||
|
|
||||||
All of the source code of applications created with Noodl are under MIT. This means you can do project specific changes to the runtime without having to redistribute your changes.
|
All of the source code of applications created with Fluxscape are under MIT. This means you can do project specific changes to the runtime without having to redistribute your changes.
|
||||||
|
|
||||||
Packaged licensed under MIT:
|
Packaged licensed under MIT:
|
||||||
- `noodl-runtime`
|
- `noodl-runtime`
|
||||||
|
|||||||
347
package-lock.json
generated
@@ -2849,9 +2849,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron/remote": {
|
"node_modules/@electron/remote": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz",
|
||||||
"integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
|
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"electron": ">= 13.0.0"
|
"electron": ">= 13.0.0"
|
||||||
}
|
}
|
||||||
@@ -25220,13 +25220,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron": {
|
"node_modules/electron": {
|
||||||
"version": "28.1.4",
|
"version": "31.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz",
|
||||||
"integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
|
"integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/get": "^2.0.0",
|
"@electron/get": "^2.0.0",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^20.9.0",
|
||||||
"extract-zip": "^2.0.1"
|
"extract-zip": "^2.0.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -25452,6 +25452,14 @@
|
|||||||
"tiny-typed-emitter": "^2.1.0"
|
"tiny-typed-emitter": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/electron/node_modules/@types/node": {
|
||||||
|
"version": "20.14.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
|
||||||
|
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/elliptic": {
|
"node_modules/elliptic": {
|
||||||
"version": "6.5.4",
|
"version": "6.5.4",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -27203,6 +27211,10 @@
|
|||||||
"react": "^15.0.2 || ^16.0.0 || ^17.0.0"
|
"react": "^15.0.2 || ^16.0.0 || ^17.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fluxscape-editor": {
|
||||||
|
"resolved": "packages/noodl-editor",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/focus-lock": {
|
"node_modules/focus-lock": {
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -35443,10 +35455,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/noodl-editor": {
|
|
||||||
"resolved": "packages/noodl-editor",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/nopt": {
|
"node_modules/nopt": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -43191,6 +43199,11 @@
|
|||||||
"version": "1.13.6",
|
"version": "1.13.6",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
|
},
|
||||||
"node_modules/unfetch": {
|
"node_modules/unfetch": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -48967,9 +48980,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"packages/noodl-editor": {
|
"packages/noodl-editor": {
|
||||||
"version": "1.0.0",
|
"name": "fluxscape-editor",
|
||||||
|
"version": "1.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^2.1.1",
|
"@electron/remote": "^2.1.2",
|
||||||
"@jaames/iro": "^5.5.2",
|
"@jaames/iro": "^5.5.2",
|
||||||
"@microsoft/fetch-event-source": "^2.0.1",
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
"@noodl/git": "file:../noodl-git",
|
"@noodl/git": "file:../noodl-git",
|
||||||
@@ -49025,7 +49039,7 @@
|
|||||||
"babel-loader": "^8.2.4",
|
"babel-loader": "^8.2.4",
|
||||||
"concurrently": "^7.4.0",
|
"concurrently": "^7.4.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"electron": "28.1.4",
|
"electron": "31.3.1",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.9.1",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-loader": "^3.1.0",
|
"html-loader": "^3.1.0",
|
||||||
@@ -53375,9 +53389,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@electron/remote": {
|
"@electron/remote": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz",
|
||||||
"integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
|
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"@electron/universal": {
|
"@electron/universal": {
|
||||||
@@ -74057,13 +74071,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "28.1.4",
|
"version": "31.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz",
|
||||||
"integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
|
"integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^2.0.0",
|
"@electron/get": "^2.0.0",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^20.9.0",
|
||||||
"extract-zip": "^2.0.1"
|
"extract-zip": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": {
|
||||||
|
"version": "20.14.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
|
||||||
|
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
|
||||||
|
"requires": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron-builder": {
|
"electron-builder": {
|
||||||
@@ -75408,6 +75432,144 @@
|
|||||||
"fbjs": "^3.0.1"
|
"fbjs": "^3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fluxscape-editor": {
|
||||||
|
"version": "file:packages/noodl-editor",
|
||||||
|
"requires": {
|
||||||
|
"@babel/core": "^7.19.1",
|
||||||
|
"@babel/preset-react": "^7.18.6",
|
||||||
|
"@electron/remote": "^2.1.2",
|
||||||
|
"@jaames/iro": "^5.5.2",
|
||||||
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
|
"@noodl/git": "file:../noodl-git",
|
||||||
|
"@noodl/noodl-parse-dashboard": "file:../noodl-parse-dashboard",
|
||||||
|
"@noodl/platform": "file:../noodl-platform",
|
||||||
|
"@noodl/platform-electron": "file:../noodl-platform-electron",
|
||||||
|
"@svgr/webpack": "^6.4.0",
|
||||||
|
"@types/checksum": "^0.1.33",
|
||||||
|
"@types/jasmine": "^4.3.0",
|
||||||
|
"@types/jquery": "^3.5.14",
|
||||||
|
"@types/react": "^17.0.50",
|
||||||
|
"@types/react-dom": "^18.0.0",
|
||||||
|
"@types/remarkable": "^2.0.3",
|
||||||
|
"@types/rimraf": "^3.0.2",
|
||||||
|
"@types/split2": "^3.2.1",
|
||||||
|
"@types/string.prototype.matchall": "^4.0.1",
|
||||||
|
"@types/underscore": "^1.11.4",
|
||||||
|
"@types/webpack-env": "^1.18.0",
|
||||||
|
"about-window": "^1.15.2",
|
||||||
|
"algoliasearch": "^4.14.2",
|
||||||
|
"archiver": "^5.3.0",
|
||||||
|
"async": "^3.2.4",
|
||||||
|
"babel-loader": "^8.2.4",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
|
"concurrently": "^7.4.0",
|
||||||
|
"css-loader": "^6.7.1",
|
||||||
|
"diff3": "0.0.4",
|
||||||
|
"dmg-license": "^1.0.11",
|
||||||
|
"electron": "31.3.1",
|
||||||
|
"electron-builder": "^24.9.1",
|
||||||
|
"electron-store": "^8.1.0",
|
||||||
|
"electron-updater": "^6.1.7",
|
||||||
|
"express": "^4.17.3",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"highlight.js": "^11.5.1",
|
||||||
|
"html-loader": "^3.1.0",
|
||||||
|
"isbinaryfile": "^5.0.0",
|
||||||
|
"md5": "^2.3.0",
|
||||||
|
"md5-file": "^5.0.0",
|
||||||
|
"mixpanel-browser": "^2.45.0",
|
||||||
|
"mkdirp": "0.5.1",
|
||||||
|
"mkdirp-sync": "0.0.2",
|
||||||
|
"monaco-editor": "^0.34.0",
|
||||||
|
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||||
|
"ncp": "^2.0.0",
|
||||||
|
"react": "^17.0.2",
|
||||||
|
"react-dom": "^17.0.0",
|
||||||
|
"react-hot-toast": "^2.4.0",
|
||||||
|
"react-instantsearch-hooks-web": "^6.38.0",
|
||||||
|
"react-json-view": "^1.21.3",
|
||||||
|
"react-rnd": "^10.3.7",
|
||||||
|
"remarkable": "^2.0.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"s3": "git+https://github.com/noodlapp/node-s3-client.git",
|
||||||
|
"sass": "^1.55.0",
|
||||||
|
"sass-loader": "^12.6.0",
|
||||||
|
"string.prototype.matchall": "^4.0.7",
|
||||||
|
"stringify": "^5.2.0",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"ts-loader": "^9.4.1",
|
||||||
|
"ts-node": "^10.7.0",
|
||||||
|
"typescript": "^4.8.3",
|
||||||
|
"underscore": "^1.13.6",
|
||||||
|
"url-loader": "^4.1.1",
|
||||||
|
"webpack": "^5.74.0",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"webpack-dev-server": "^4.11.1",
|
||||||
|
"webpack-merge": "^5.8.0",
|
||||||
|
"websocket-stream": "^5.5.2",
|
||||||
|
"ws": "^8.9.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@webpack-cli/configtest": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@webpack-cli/info": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"envinfo": "^7.7.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@webpack-cli/serve": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"rechoir": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"resolve": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webpack-cli": {
|
||||||
|
"version": "4.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
|
||||||
|
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@discoveryjs/json-ext": "^0.5.0",
|
||||||
|
"@webpack-cli/configtest": "^1.2.0",
|
||||||
|
"@webpack-cli/info": "^1.5.0",
|
||||||
|
"@webpack-cli/serve": "^1.7.0",
|
||||||
|
"colorette": "^2.0.14",
|
||||||
|
"commander": "^7.0.0",
|
||||||
|
"cross-spawn": "^7.0.3",
|
||||||
|
"fastest-levenshtein": "^1.0.12",
|
||||||
|
"import-local": "^3.0.2",
|
||||||
|
"interpret": "^2.2.0",
|
||||||
|
"rechoir": "^0.7.0",
|
||||||
|
"webpack-merge": "^5.7.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"focus-lock": {
|
"focus-lock": {
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -80983,144 +81145,6 @@
|
|||||||
"version": "2.0.10",
|
"version": "2.0.10",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"noodl-editor": {
|
|
||||||
"version": "file:packages/noodl-editor",
|
|
||||||
"requires": {
|
|
||||||
"@babel/core": "^7.19.1",
|
|
||||||
"@babel/preset-react": "^7.18.6",
|
|
||||||
"@electron/remote": "^2.1.1",
|
|
||||||
"@jaames/iro": "^5.5.2",
|
|
||||||
"@microsoft/fetch-event-source": "^2.0.1",
|
|
||||||
"@noodl/git": "file:../noodl-git",
|
|
||||||
"@noodl/noodl-parse-dashboard": "file:../noodl-parse-dashboard",
|
|
||||||
"@noodl/platform": "file:../noodl-platform",
|
|
||||||
"@noodl/platform-electron": "file:../noodl-platform-electron",
|
|
||||||
"@svgr/webpack": "^6.4.0",
|
|
||||||
"@types/checksum": "^0.1.33",
|
|
||||||
"@types/jasmine": "^4.3.0",
|
|
||||||
"@types/jquery": "^3.5.14",
|
|
||||||
"@types/react": "^17.0.50",
|
|
||||||
"@types/react-dom": "^18.0.0",
|
|
||||||
"@types/remarkable": "^2.0.3",
|
|
||||||
"@types/rimraf": "^3.0.2",
|
|
||||||
"@types/split2": "^3.2.1",
|
|
||||||
"@types/string.prototype.matchall": "^4.0.1",
|
|
||||||
"@types/underscore": "^1.11.4",
|
|
||||||
"@types/webpack-env": "^1.18.0",
|
|
||||||
"about-window": "^1.15.2",
|
|
||||||
"algoliasearch": "^4.14.2",
|
|
||||||
"archiver": "^5.3.0",
|
|
||||||
"async": "^3.2.4",
|
|
||||||
"babel-loader": "^8.2.4",
|
|
||||||
"classnames": "^2.3.2",
|
|
||||||
"concurrently": "^7.4.0",
|
|
||||||
"css-loader": "^6.7.1",
|
|
||||||
"diff3": "0.0.4",
|
|
||||||
"dmg-license": "^1.0.11",
|
|
||||||
"electron": "28.1.4",
|
|
||||||
"electron-builder": "^24.9.1",
|
|
||||||
"electron-store": "^8.1.0",
|
|
||||||
"electron-updater": "^6.1.7",
|
|
||||||
"express": "^4.17.3",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"highlight.js": "^11.5.1",
|
|
||||||
"html-loader": "^3.1.0",
|
|
||||||
"isbinaryfile": "^5.0.0",
|
|
||||||
"md5": "^2.3.0",
|
|
||||||
"md5-file": "^5.0.0",
|
|
||||||
"mixpanel-browser": "^2.45.0",
|
|
||||||
"mkdirp": "0.5.1",
|
|
||||||
"mkdirp-sync": "0.0.2",
|
|
||||||
"monaco-editor": "^0.34.0",
|
|
||||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
|
||||||
"ncp": "^2.0.0",
|
|
||||||
"react": "^17.0.2",
|
|
||||||
"react-dom": "^17.0.0",
|
|
||||||
"react-hot-toast": "^2.4.0",
|
|
||||||
"react-instantsearch-hooks-web": "^6.38.0",
|
|
||||||
"react-json-view": "^1.21.3",
|
|
||||||
"react-rnd": "^10.3.7",
|
|
||||||
"remarkable": "^2.0.1",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"s3": "git+https://github.com/noodlapp/node-s3-client.git",
|
|
||||||
"sass": "^1.55.0",
|
|
||||||
"sass-loader": "^12.6.0",
|
|
||||||
"string.prototype.matchall": "^4.0.7",
|
|
||||||
"stringify": "^5.2.0",
|
|
||||||
"style-loader": "^3.3.1",
|
|
||||||
"ts-loader": "^9.4.1",
|
|
||||||
"ts-node": "^10.7.0",
|
|
||||||
"typescript": "^4.8.3",
|
|
||||||
"underscore": "^1.13.6",
|
|
||||||
"url-loader": "^4.1.1",
|
|
||||||
"webpack": "^5.74.0",
|
|
||||||
"webpack-cli": "^4.10.0",
|
|
||||||
"webpack-dev-server": "^4.11.1",
|
|
||||||
"webpack-merge": "^5.8.0",
|
|
||||||
"websocket-stream": "^5.5.2",
|
|
||||||
"ws": "^8.9.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@webpack-cli/configtest": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"@webpack-cli/info": {
|
|
||||||
"version": "1.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
|
|
||||||
"integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"envinfo": "^7.7.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@webpack-cli/serve": {
|
|
||||||
"version": "1.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
|
|
||||||
"integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"commander": {
|
|
||||||
"version": "7.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
|
||||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"rechoir": {
|
|
||||||
"version": "0.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
|
|
||||||
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"resolve": "^1.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"webpack-cli": {
|
|
||||||
"version": "4.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
|
|
||||||
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@discoveryjs/json-ext": "^0.5.0",
|
|
||||||
"@webpack-cli/configtest": "^1.2.0",
|
|
||||||
"@webpack-cli/info": "^1.5.0",
|
|
||||||
"@webpack-cli/serve": "^1.7.0",
|
|
||||||
"colorette": "^2.0.14",
|
|
||||||
"commander": "^7.0.0",
|
|
||||||
"cross-spawn": "^7.0.3",
|
|
||||||
"fastest-levenshtein": "^1.0.12",
|
|
||||||
"import-local": "^3.0.2",
|
|
||||||
"interpret": "^2.2.0",
|
|
||||||
"rechoir": "^0.7.0",
|
|
||||||
"webpack-merge": "^5.7.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nopt": {
|
"nopt": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -86403,6 +86427,11 @@
|
|||||||
"underscore": {
|
"underscore": {
|
||||||
"version": "1.13.6"
|
"version": "1.13.6"
|
||||||
},
|
},
|
||||||
|
"undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
|
},
|
||||||
"unfetch": {
|
"unfetch": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"graph": "npx nx graph",
|
"graph": "npx nx graph",
|
||||||
"ci:prepare:editor": "ts-node ./scripts/ci-editor-prepare.ts",
|
"ci:prepare:editor": "ts-node ./scripts/ci-editor-prepare.ts",
|
||||||
"ci:build:viewer": "lerna exec --scope @noodl/noodl-viewer-react -- npm run build",
|
"ci:build:viewer": "lerna exec --scope @noodl/noodl-viewer-react -- npm run build",
|
||||||
"ci:build:editor": "lerna exec --scope noodl-editor -- npm run ci:build",
|
"ci:build:editor": "lerna exec --scope fluxscape-editor -- npm run ci:build",
|
||||||
"build:editor": "ts-node ./scripts/build-editor.ts",
|
"build:editor": "ts-node ./scripts/build-editor.ts",
|
||||||
"build:editor:_viewer": "ts-node ./scripts/noodl-editor/build-viewer.ts",
|
"build:editor:_viewer": "ts-node ./scripts/noodl-editor/build-viewer.ts",
|
||||||
"build:editor:_editor": "ts-node ./scripts/noodl-editor/build-editor.ts",
|
"build:editor:_editor": "ts-node ./scripts/noodl-editor/build-editor.ts",
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"build:cloud-runtime": "lerna run build --scope @noodl/cloud-runtime --stream && lerna run build:pack --scope @noodl/cloud-runtime --stream",
|
"build:cloud-runtime": "lerna run build --scope @noodl/cloud-runtime --stream && lerna run build:pack --scope @noodl/cloud-runtime --stream",
|
||||||
"start:storybook": "lerna exec --scope @noodl/noodl-core-ui -- npm run start",
|
"start:storybook": "lerna exec --scope @noodl/noodl-core-ui -- npm run start",
|
||||||
"start:viewer": "lerna run start --scope @noodl/noodl-viewer-react --stream",
|
"start:viewer": "lerna run start --scope @noodl/noodl-viewer-react --stream",
|
||||||
"start:editor": "lerna run start --scope noodl-editor --stream",
|
"start:editor": "lerna run start --scope fluxscape-editor --stream",
|
||||||
"dev": "ts-node ./scripts/start.ts",
|
"dev": "ts-node ./scripts/start.ts",
|
||||||
"start": "ts-node ./scripts/start.ts -- --build-viewer",
|
"start": "ts-node ./scripts/start.ts -- --build-viewer",
|
||||||
"test:editor": "ts-node ./scripts/test-editor.ts",
|
"test:editor": "ts-node ./scripts/test-editor.ts",
|
||||||
|
|||||||
@@ -9,6 +9,11 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
|
&.is-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
&.has-backdrop {
|
&.has-backdrop {
|
||||||
background-color: var(--theme-color-bg-1-transparent);
|
background-color: var(--theme-color-bg-1-transparent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export interface BaseDialogProps extends UnsafeStyleProps {
|
|||||||
isVisible?: boolean;
|
isVisible?: boolean;
|
||||||
hasBackdrop?: boolean;
|
hasBackdrop?: boolean;
|
||||||
hasArrow?: boolean;
|
hasArrow?: boolean;
|
||||||
|
alwaysMounted?: boolean;
|
||||||
|
|
||||||
children?: Slot;
|
children?: Slot;
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ export function CoreBaseDialog({
|
|||||||
isVisible,
|
isVisible,
|
||||||
hasBackdrop,
|
hasBackdrop,
|
||||||
hasArrow,
|
hasArrow,
|
||||||
|
alwaysMounted,
|
||||||
|
|
||||||
children,
|
children,
|
||||||
|
|
||||||
@@ -261,7 +263,7 @@ export function CoreBaseDialog({
|
|||||||
}
|
}
|
||||||
}, [background]);
|
}, [background]);
|
||||||
|
|
||||||
if (!isVisible) return null;
|
if (!isVisible && !alwaysMounted) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -270,6 +272,7 @@ export function CoreBaseDialog({
|
|||||||
hasBackdrop && css['has-backdrop'],
|
hasBackdrop && css['has-backdrop'],
|
||||||
isLockingScroll && css['is-locking-scroll'],
|
isLockingScroll && css['is-locking-scroll'],
|
||||||
typeof triggerRef === 'undefined' && css['is-centered'],
|
typeof triggerRef === 'undefined' && css['is-centered'],
|
||||||
|
!isVisible && css['is-hidden'],
|
||||||
css[variant]
|
css[variant]
|
||||||
)}
|
)}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "noodl-editor",
|
"name": "fluxscape-editor",
|
||||||
"productName": "Fluxscape",
|
"productName": "Fluxscape",
|
||||||
"description": "Node-Based App Builder for Scalability & Rapid Development, a fork of Noodl",
|
"description": "Node-Based App Builder for Scalability & Rapid Development, a fork of Noodl",
|
||||||
"author": "Fluxscape <contact@fluxscape.io>",
|
"author": "Fluxscape <contact@fluxscape.io>",
|
||||||
"homepage": "https://fluxscape.io",
|
"homepage": "https://fluxscape.io",
|
||||||
"version": "1.0.0",
|
"version": "1.2.0",
|
||||||
"main": "src/main/main.js",
|
"main": "src/main/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npx ts-node -P ./tsconfig.build.json ./scripts/build.ts",
|
"build": "npx ts-node -P ./tsconfig.build.json ./scripts/build.ts",
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^2.1.1",
|
"@electron/remote": "^2.1.2",
|
||||||
"@jaames/iro": "^5.5.2",
|
"@jaames/iro": "^5.5.2",
|
||||||
"@microsoft/fetch-event-source": "^2.0.1",
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
"@noodl/git": "file:../noodl-git",
|
"@noodl/git": "file:../noodl-git",
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
"babel-loader": "^8.2.4",
|
"babel-loader": "^8.2.4",
|
||||||
"concurrently": "^7.4.0",
|
"concurrently": "^7.4.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"electron": "28.1.4",
|
"electron": "31.3.1",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.9.1",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-loader": "^3.1.0",
|
"html-loader": "^3.1.0",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3zm-9-3.82l-2.09-2.09L6.5 13.5 10 17l6.01-6.01-1.41-1.41z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 526 B |
|
Before Width: | Height: | Size: 540 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 175 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0V0z"/><path opacity=".87" d="M0 0h24v24H0V0z"/></g><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3zM23 3.01H1V9h2V4.99h18v14.03H3V15H1v5.99h22V3.01zM11 16l4-4-4-4v3H1v2h10v3z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 408 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M4 6H2v16h16v-2H4V6zm18-4H6v16h16V2zm-3 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 220 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0V0z"/><path opacity=".87" d="M0 0h24v24H0V0z"/></g><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7zm-4 6h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 360 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19.44 12.99l-.01.02c.04-.33.08-.67.08-1.01 0-.34-.03-.66-.07-.99l.01.02 2.44-1.92-2.43-4.22-2.87 1.16.01.01c-.52-.4-1.09-.74-1.71-1h.01L14.44 2H9.57l-.44 3.07h.01c-.62.26-1.19.6-1.71 1l.01-.01-2.88-1.17-2.44 4.22 2.44 1.92.01-.02c-.04.33-.07.65-.07.99 0 .34.03.68.08 1.01l-.01-.02-2.1 1.65-.33.26 2.43 4.2 2.88-1.15-.02-.04c.53.41 1.1.75 1.73 1.01h-.03L9.58 22h4.85s.03-.18.06-.42l.38-2.65h-.01c.62-.26 1.2-.6 1.73-1.01l-.02.04 2.88 1.15 2.43-4.2s-.14-.12-.33-.26l-2.11-1.66zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 701 B |
|
Before Width: | Height: | Size: 522 KiB |
|
Before Width: | Height: | Size: 522 KiB |
@@ -6,7 +6,7 @@ import { EventDispatcher } from '../../shared/utils/EventDispatcher';
|
|||||||
import ProjectModules from '../../shared/utils/projectmodules';
|
import ProjectModules from '../../shared/utils/projectmodules';
|
||||||
import { NodeLibrary } from './models/nodelibrary';
|
import { NodeLibrary } from './models/nodelibrary';
|
||||||
import { ProjectModel } from './models/projectmodel';
|
import { ProjectModel } from './models/projectmodel';
|
||||||
import { WarningsModel } from './models/warningsmodel';
|
import { WarningRef, WarningsModel } from './models/warningsmodel';
|
||||||
import DebugInspector from './utils/debuginspector';
|
import DebugInspector from './utils/debuginspector';
|
||||||
import * as Exporter from './utils/exporter';
|
import * as Exporter from './utils/exporter';
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ export class ViewerConnection extends Model {
|
|||||||
} else if (request.cmd === 'showwarning' && request.type === 'viewer') {
|
} else if (request.cmd === 'showwarning' && request.type === 'viewer') {
|
||||||
const content = JSON.parse(request.content);
|
const content = JSON.parse(request.content);
|
||||||
if (ProjectModel.instance !== undefined) {
|
if (ProjectModel.instance !== undefined) {
|
||||||
const ref = {
|
const ref: WarningRef = {
|
||||||
component: ProjectModel.instance.getComponentWithName(content.componentName),
|
component: ProjectModel.instance.getComponentWithName(content.componentName),
|
||||||
node: ProjectModel.instance.findNodeWithId(content.nodeId),
|
node: ProjectModel.instance.findNodeWithId(content.nodeId),
|
||||||
key: content.key,
|
key: content.key,
|
||||||
@@ -124,7 +124,7 @@ export class ViewerConnection extends Model {
|
|||||||
}
|
}
|
||||||
} else if (request.cmd === 'clearwarnings' && request.type === 'viewer') {
|
} else if (request.cmd === 'clearwarnings' && request.type === 'viewer') {
|
||||||
const content = JSON.parse(request.content);
|
const content = JSON.parse(request.content);
|
||||||
const ref = {
|
const ref: WarningRef = {
|
||||||
component: ProjectModel.instance.getComponentWithName(content.componentName),
|
component: ProjectModel.instance.getComponentWithName(content.componentName),
|
||||||
node: ProjectModel.instance.findNodeWithId(content.nodeId)
|
node: ProjectModel.instance.findNodeWithId(content.nodeId)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { type ComponentModel } from '@noodl-models/componentmodel';
|
||||||
|
import { type NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
||||||
|
import { type NodeLibraryNodeType } from '@noodl-models/nodelibrary';
|
||||||
|
import { type Slot } from '@noodl-core-ui/types/global';
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
|
import { EventDispatcher } from '../../../../shared/utils/EventDispatcher';
|
||||||
|
|
||||||
|
export type NodeReference = {
|
||||||
|
type: NodeLibraryNodeType | undefined;
|
||||||
|
displayName: string;
|
||||||
|
referenaces: {
|
||||||
|
displayName: string;
|
||||||
|
node?: NodeGraphNode;
|
||||||
|
component: ComponentModel;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface NodeReferencesContext {
|
||||||
|
nodeReferences: NodeReference[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeReferencesContext = createContext<NodeReferencesContext>({
|
||||||
|
nodeReferences: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Since all the editor code is not written in React we need a way to be able to
|
||||||
|
// access this information outside of React too.
|
||||||
|
let HACK_nodeReferences: NodeReference[] = [];
|
||||||
|
export function HACK_findNodeReference(componentName: string): NodeReference | undefined {
|
||||||
|
return HACK_nodeReferences.find(x => x.type?.fullName === componentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface NodeReferencesContextProps {
|
||||||
|
children: Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NodeReferencesContextProvider({ children }: NodeReferencesContextProps) {
|
||||||
|
const [group] = useState({});
|
||||||
|
const [nodeReferences, setNodeReferences] = useState<NodeReference[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function updateIndex() {
|
||||||
|
const types: { [key: string]: NodeReference['type'] } = {};
|
||||||
|
const references = new Map<string, NodeReference['referenaces']>();
|
||||||
|
|
||||||
|
function handleComponent(component: ComponentModel) {
|
||||||
|
component.forEachNode((node: NodeGraphNode) => {
|
||||||
|
const name = node.type.name;
|
||||||
|
|
||||||
|
// Add the reference
|
||||||
|
references.set(name, [
|
||||||
|
...(references.get(name) || []),
|
||||||
|
{
|
||||||
|
displayName: component.displayName || component.name,
|
||||||
|
node,
|
||||||
|
component
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Repeater
|
||||||
|
if (name === 'For Each' && node.parameters.template) {
|
||||||
|
const templateComponent = ProjectModel.instance.getComponentWithName(node.parameters.template);
|
||||||
|
|
||||||
|
if (templateComponent) {
|
||||||
|
references.set(templateComponent.fullName, [
|
||||||
|
...(references.get(templateComponent.fullName) || []),
|
||||||
|
{
|
||||||
|
displayName: component.displayName || component.name,
|
||||||
|
node,
|
||||||
|
component
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
handleComponent(templateComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some metadata for this node if we dont have it yet.
|
||||||
|
if (!types[name]) {
|
||||||
|
types[name] = node.type;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop all the nodes in the project
|
||||||
|
ProjectModel.instance.forEachComponent(handleComponent);
|
||||||
|
|
||||||
|
// Combine the result to look a little better.
|
||||||
|
const results: NodeReference[] = Array.from(references.keys())
|
||||||
|
.map((key) => ({
|
||||||
|
type: types[key],
|
||||||
|
displayName: types[key]?.displayName || key,
|
||||||
|
referenaces: references.get(key)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => b.referenaces.length - a.referenaces.length);
|
||||||
|
|
||||||
|
HACK_nodeReferences = results;
|
||||||
|
setNodeReferences(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIndex();
|
||||||
|
|
||||||
|
EventDispatcher.instance.on(
|
||||||
|
[
|
||||||
|
'Model.nodeAdded',
|
||||||
|
'Model.nodeRemoved',
|
||||||
|
'Model.componentAdded',
|
||||||
|
'Model.componentRemoved',
|
||||||
|
'Model.componentRenamed'
|
||||||
|
],
|
||||||
|
updateIndex,
|
||||||
|
group
|
||||||
|
);
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
EventDispatcher.instance.off(group);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodeReferencesContext.Provider
|
||||||
|
value={{
|
||||||
|
nodeReferences,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</NodeReferencesContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNodeReferencesContext() {
|
||||||
|
const context = useContext(NodeReferencesContext);
|
||||||
|
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useNodeReferencesContext must be a child of NodeReferencesContextProvider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './NodeReferencesContext';
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { CloudService } from '@noodl-models/CloudServices';
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
|
import { setCloudServices } from '@noodl-models/projectmodel.editor';
|
||||||
|
|
||||||
|
import { ToastLayer } from '../../../views/ToastLayer';
|
||||||
|
|
||||||
|
export type Command = {
|
||||||
|
kind: 'cloud-service';
|
||||||
|
use?: boolean;
|
||||||
|
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
endpoint: string;
|
||||||
|
appId: string;
|
||||||
|
masterKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function execute(command: Command, _event: MessageEvent) {
|
||||||
|
const environment = await getOrCreate(command);
|
||||||
|
|
||||||
|
if (command.use) {
|
||||||
|
setCloudServices(ProjectModel.instance, environment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getOrCreate(command: Command) {
|
||||||
|
const cloudServices = await CloudService.instance.backend.fetch();
|
||||||
|
const environment = cloudServices.find((c) => c.url === command.endpoint);
|
||||||
|
if (environment) {
|
||||||
|
if (
|
||||||
|
environment.name !== command.name ||
|
||||||
|
environment.description !== command.description ||
|
||||||
|
environment.appId !== command.appId ||
|
||||||
|
environment.masterKey !== command.masterKey
|
||||||
|
) {
|
||||||
|
await CloudService.instance.backend.update({
|
||||||
|
id: environment.id,
|
||||||
|
url: environment.url,
|
||||||
|
|
||||||
|
// Update the existing environment
|
||||||
|
name: command.name,
|
||||||
|
description: command.description,
|
||||||
|
appId: command.appId,
|
||||||
|
masterKey: command.masterKey
|
||||||
|
});
|
||||||
|
|
||||||
|
ToastLayer.showSuccess(`Cloud service "${command.name}" updated.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: environment.id,
|
||||||
|
endpoint: environment.url,
|
||||||
|
appId: command.appId
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const create = await CloudService.instance.backend.create({
|
||||||
|
name: command.name,
|
||||||
|
description: command.description,
|
||||||
|
appId: command.appId,
|
||||||
|
url: command.endpoint,
|
||||||
|
masterKey: command.masterKey
|
||||||
|
});
|
||||||
|
|
||||||
|
ToastLayer.showSuccess(`Cloud service "${command.name}" added successfully.`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: create.id,
|
||||||
|
endpoint: create.url,
|
||||||
|
appId: create.appId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
|
import * as CloudService from './commands/cloud-service';
|
||||||
import * as Notify from './commands/notify';
|
import * as Notify from './commands/notify';
|
||||||
import * as UploadAwsS3 from './commands/upload-aws-s3';
|
import * as UploadAwsS3 from './commands/upload-aws-s3';
|
||||||
|
|
||||||
type IFrameCommand = Notify.Command | UploadAwsS3.Command;
|
type IFrameCommand = Notify.Command | UploadAwsS3.Command | CloudService.Command;
|
||||||
|
|
||||||
const commands: Record<IFrameCommand['kind'], (command: IFrameCommand, event: MessageEvent) => Promise<void>> = {
|
const commands: Record<IFrameCommand['kind'], (command: IFrameCommand, event: MessageEvent) => Promise<void>> = {
|
||||||
notify: Notify.execute,
|
notify: Notify.execute,
|
||||||
'upload-aws-s3': UploadAwsS3.execute
|
'upload-aws-s3': UploadAwsS3.execute,
|
||||||
|
'cloud-service': CloudService.execute
|
||||||
};
|
};
|
||||||
|
|
||||||
export function commandEventHandler(event: MessageEvent) {
|
export function commandEventHandler(event: MessageEvent) {
|
||||||
|
|||||||
@@ -57,10 +57,13 @@ export class CloudFunctionAdapter extends NodeTypeAdapter {
|
|||||||
|
|
||||||
// Collect all cloud function components
|
// Collect all cloud function components
|
||||||
const functionRequestNodes = ProjectModel.instance.getNodesWithType('noodl.cloud.request');
|
const functionRequestNodes = ProjectModel.instance.getNodesWithType('noodl.cloud.request');
|
||||||
const functions = functionRequestNodes.map((r) => {
|
const functions = functionRequestNodes
|
||||||
const component = r.owner.owner;
|
.map((r) => {
|
||||||
return component.fullName;
|
const component = r.owner.owner;
|
||||||
});
|
return component.fullName;
|
||||||
|
})
|
||||||
|
// Remove all the Cloud Trigger functions
|
||||||
|
.filter((x) => !x.startsWith('/#__cloud__/__noodl_cloud_triggers__'));
|
||||||
|
|
||||||
ports.push({
|
ports.push({
|
||||||
plug: 'input',
|
plug: 'input',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
||||||
import { WarningsModel } from '@noodl-models/warningsmodel';
|
import { type Warning, WarningsModel } from '@noodl-models/warningsmodel';
|
||||||
|
|
||||||
import { ProjectModel } from '../projectmodel';
|
import { ProjectModel } from '../projectmodel';
|
||||||
import NodeTypeAdapter from './NodeTypeAdapter';
|
import NodeTypeAdapter from './NodeTypeAdapter';
|
||||||
@@ -103,7 +103,7 @@ export class RouterNavigateAdapter extends NodeTypeAdapter {
|
|||||||
|
|
||||||
const hasValidTarget = target && pageComponents.includes(target);
|
const hasValidTarget = target && pageComponents.includes(target);
|
||||||
|
|
||||||
const warning =
|
const warning: Warning =
|
||||||
hasValidTarget === false
|
hasValidTarget === false
|
||||||
? {
|
? {
|
||||||
message: "The target page doesn't belong to the target router",
|
message: "The target page doesn't belong to the target router",
|
||||||
|
|||||||
@@ -458,11 +458,11 @@ export class VariantModel extends Model {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else if (c.type === 'defaultStateTransition') {
|
} else if (c.type === 'defaultStateTransition') {
|
||||||
var state =
|
const state =
|
||||||
this.getType().visualStates !== undefined
|
this.getType().visualStates !== undefined
|
||||||
? this.getType().visualStates.find((s) => s.name === c.state)
|
? this.getType().visualStates.find((s) => s.name === c.state)
|
||||||
: undefined;
|
: undefined;
|
||||||
var stateName = state !== undefined ? state.label : c.state;
|
const stateName = state !== undefined ? state.label : c.state;
|
||||||
|
|
||||||
WarningsModel.instance.setWarning(
|
WarningsModel.instance.setWarning(
|
||||||
{ key: 'variant-dst-conflict-' + this.name + '-' + this.getType().fullName + '-' + c.state },
|
{ key: 'variant-dst-conflict-' + this.name + '-' + this.getType().fullName + '-' + c.state },
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export class NodeGraphNode extends Model {
|
|||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
|
|
||||||
private _variant: TSFixme;
|
private _variant: TSFixme;
|
||||||
private _label: TSFixme;
|
_label: TSFixme;
|
||||||
private _type: TSFixme;
|
private _type: TSFixme;
|
||||||
private _ports: TSFixme;
|
private _ports: TSFixme;
|
||||||
|
|
||||||
|
|||||||
@@ -1167,6 +1167,28 @@ EventDispatcher.instance.on(
|
|||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
NodeLibrary.instance.on('libraryUpdated', () => {
|
||||||
|
const library = NodeLibrary.instance.library;
|
||||||
|
|
||||||
|
// Check if we have any data from the browser
|
||||||
|
if (Object.keys(library?.nodeIndex || {}).length > 0) {
|
||||||
|
const filepath = filesystem.join(ProjectModel.instance._retainedProjectDirectory, 'nodelibrary.json');
|
||||||
|
|
||||||
|
const compactLibrary = {
|
||||||
|
typecasts: library.typecasts,
|
||||||
|
dynamicports: library.dynamicports,
|
||||||
|
nodetypes: library.nodetypes,
|
||||||
|
// NOTE: Let's save the node index for now, most likely this is something we will just ignore.
|
||||||
|
nodeIndex: library.nodeIndex,
|
||||||
|
projectsettings: library.projectsettings
|
||||||
|
};
|
||||||
|
|
||||||
|
filesystem.writeJson(filepath, compactLibrary).then(() => {
|
||||||
|
console.log('saved nodelibrary.json');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function saveProject() {
|
function saveProject() {
|
||||||
if (!ProjectModel.instance) return;
|
if (!ProjectModel.instance) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,42 @@
|
|||||||
import { ComponentModel } from '@noodl-models/componentmodel';
|
|
||||||
import { ProjectModel } from '@noodl-models/projectmodel';
|
|
||||||
import { toArray } from 'underscore';
|
import { toArray } from 'underscore';
|
||||||
|
|
||||||
|
import type { ComponentModel } from '@noodl-models/componentmodel';
|
||||||
|
|
||||||
import Model from '../../../shared/model';
|
import Model from '../../../shared/model';
|
||||||
|
import type { NodeGraphNode } from './nodegraphmodel';
|
||||||
import { NodeLibrary } from './nodelibrary';
|
import { NodeLibrary } from './nodelibrary';
|
||||||
|
|
||||||
|
export type WarningLabel = 'warning' | 'error';
|
||||||
|
|
||||||
|
export type Warning =
|
||||||
|
| {
|
||||||
|
type?: string;
|
||||||
|
level?: WarningLabel;
|
||||||
|
message: string;
|
||||||
|
showGlobally?: boolean;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'conflict' | 'conflict-source-code';
|
||||||
|
level?: WarningLabel;
|
||||||
|
message: string;
|
||||||
|
showGlobally?: boolean;
|
||||||
|
conflictMetadata: {
|
||||||
|
parameter: string;
|
||||||
|
ours: string;
|
||||||
|
theirs: string;
|
||||||
|
};
|
||||||
|
onDismiss: () => void;
|
||||||
|
onUseTheirs: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WarningRef = {
|
||||||
|
key?: string;
|
||||||
|
component?: ComponentModel;
|
||||||
|
connection?: TSFixme;
|
||||||
|
node?: NodeGraphNode;
|
||||||
|
isFromViewer?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first level of the warnings object is component name
|
* The first level of the warnings object is component name
|
||||||
* Second is the connection / node identifier
|
* Second is the connection / node identifier
|
||||||
@@ -14,7 +47,7 @@ interface Warnings {
|
|||||||
[node_connection_id: string]: {
|
[node_connection_id: string]: {
|
||||||
[warningKey: string]: {
|
[warningKey: string]: {
|
||||||
ref: TSFixme;
|
ref: TSFixme;
|
||||||
warning: TSFixme;
|
warning: Warning;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -24,7 +57,7 @@ export class WarningsModel extends Model {
|
|||||||
public static instance = new WarningsModel();
|
public static instance = new WarningsModel();
|
||||||
|
|
||||||
private warnings: Warnings = {};
|
private warnings: Warnings = {};
|
||||||
private notifyChangedScheduled: boolean = false;
|
private notifyChangedScheduled = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -35,10 +68,12 @@ export class WarningsModel extends Model {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setWarning(ref, warning) {
|
public setWarning(ref: WarningRef, warning: Warning) {
|
||||||
var w = this.getWarningsForRef(ref, warning !== undefined);
|
const w = this.getWarningsForRef(ref, warning !== undefined);
|
||||||
if (!warning) {
|
if (!warning) {
|
||||||
if (w) delete w[ref.key];
|
if (w) {
|
||||||
|
delete w[ref.key];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
warning.level = warning.level || 'warning';
|
warning.level = warning.level || 'warning';
|
||||||
w[ref.key] = { ref: ref, warning: warning };
|
w[ref.key] = { ref: ref, warning: warning };
|
||||||
@@ -47,31 +82,34 @@ export class WarningsModel extends Model {
|
|||||||
this.scheduleNotifyChanged();
|
this.scheduleNotifyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearWarningsForRef(ref) {
|
public clearWarningsForRef(ref: WarningRef) {
|
||||||
var w = this.getWarningsForRef(ref);
|
const w = this.getWarningsForRef(ref);
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
|
||||||
for (var i in w) delete w[i];
|
for (const i in w) {
|
||||||
|
delete w[i];
|
||||||
|
}
|
||||||
|
|
||||||
this.scheduleNotifyChanged();
|
this.scheduleNotifyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearAllWarningsForComponent(component) {
|
public clearAllWarningsForComponent(component: ComponentModel) {
|
||||||
const warnings = this.warnings[component.name];
|
const warnings = this.warnings[component.name];
|
||||||
|
|
||||||
if (!warnings) return;
|
if (!warnings) return;
|
||||||
|
|
||||||
for (let i in warnings) delete warnings[i];
|
for (const i in warnings) {
|
||||||
|
delete warnings[i];
|
||||||
|
}
|
||||||
|
|
||||||
this.scheduleNotifyChanged();
|
this.scheduleNotifyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearWarningsForRefMatching(matchCb) {
|
public clearWarningsForRefMatching(matchFn: (ref: TSFixme) => boolean) {
|
||||||
for (const cw of Object.values(this.warnings)) {
|
for (const cw of Object.values(this.warnings)) {
|
||||||
for (const ws of Object.values(cw)) {
|
for (const ws of Object.values(cw)) {
|
||||||
for (const key in ws) {
|
for (const key in ws) {
|
||||||
const w = ws[key];
|
const w = ws[key];
|
||||||
if (matchCb(w.ref)) {
|
if (matchFn(w.ref)) {
|
||||||
delete ws[key];
|
delete ws[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,15 +125,14 @@ export class WarningsModel extends Model {
|
|||||||
this.scheduleNotifyChanged();
|
this.scheduleNotifyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getWarnings(ref) {
|
public getWarnings(ref: WarningRef) {
|
||||||
var w = this.getWarningsForRef(ref);
|
const w = this.getWarningsForRef(ref);
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
|
||||||
if (Object.keys(w).length === 0) return;
|
if (Object.keys(w).length === 0) return;
|
||||||
|
|
||||||
// Create short message for hover
|
// Create short message for hover
|
||||||
var messages = [];
|
const messages = [];
|
||||||
for (var k in w) {
|
for (const k in w) {
|
||||||
if (w[k].warning) messages.push(w[k].warning.message);
|
if (w[k].warning) messages.push(w[k].warning.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,13 +143,13 @@ export class WarningsModel extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public forEachWarningInComponent(c, callback, args) {
|
public forEachWarningInComponent(c, callback, args) {
|
||||||
var cw = this.warnings[c ? c.name : '/'];
|
const cw = this.warnings[c ? c.name : '/'];
|
||||||
if (!cw) return;
|
if (!cw) return;
|
||||||
|
|
||||||
for (var ref in cw) {
|
for (const ref in cw) {
|
||||||
var ws = cw[ref];
|
const ws = cw[ref];
|
||||||
|
|
||||||
for (var w in ws) {
|
for (const w in ws) {
|
||||||
if (!args || !args.levels || args.levels.indexOf(ws[w].warning.level) !== -1) {
|
if (!args || !args.levels || args.levels.indexOf(ws[w].warning.level) !== -1) {
|
||||||
callback(ws[w]);
|
callback(ws[w]);
|
||||||
}
|
}
|
||||||
@@ -121,7 +158,7 @@ export class WarningsModel extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getAllWarningsForComponent(c, args) {
|
public getAllWarningsForComponent(c, args) {
|
||||||
var warnings = [];
|
const warnings = [];
|
||||||
this.forEachWarningInComponent(
|
this.forEachWarningInComponent(
|
||||||
c,
|
c,
|
||||||
function (warning) {
|
function (warning) {
|
||||||
@@ -152,15 +189,15 @@ export class WarningsModel extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getTotalNumberOfWarnings(args) {
|
public getTotalNumberOfWarnings(args) {
|
||||||
var total = 0;
|
let total = 0;
|
||||||
for (var key in this.warnings) {
|
for (const key in this.warnings) {
|
||||||
total += this.getNumberOfWarningsForComponent({ name: key }, args);
|
total += this.getNumberOfWarningsForComponent({ name: key }, args);
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTotalNumberOfWarningsMatching(matchCb) {
|
public getTotalNumberOfWarningsMatching(matchCb) {
|
||||||
var total = 0;
|
let total = 0;
|
||||||
this.forEachWarning((c, ref, key, warning) => {
|
this.forEachWarning((c, ref, key, warning) => {
|
||||||
if (matchCb(key, ref, warning)) total++;
|
if (matchCb(key, ref, warning)) total++;
|
||||||
});
|
});
|
||||||
@@ -168,16 +205,16 @@ export class WarningsModel extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public forEachWarning(callback: (c: string, ref, key, warning) => void) {
|
public forEachWarning(callback: (c: string, ref, key, warning) => void) {
|
||||||
var w = this.warnings;
|
const w = this.warnings;
|
||||||
for (const c in w) {
|
for (const c in w) {
|
||||||
// Loop over all components
|
// Loop over all components
|
||||||
var _w = w[c];
|
const _w = w[c];
|
||||||
for (const ref in _w) {
|
for (const ref in _w) {
|
||||||
// Loop over all refs
|
// Loop over all refs
|
||||||
var __w = _w[ref];
|
const __w = _w[ref];
|
||||||
for (const key in __w) {
|
for (const key in __w) {
|
||||||
// Loop over all keys
|
// Loop over all keys
|
||||||
var warning = __w[key];
|
const warning = __w[key];
|
||||||
|
|
||||||
callback(c, ref, key, warning);
|
callback(c, ref, key, warning);
|
||||||
}
|
}
|
||||||
@@ -195,12 +232,12 @@ export class WarningsModel extends Model {
|
|||||||
// node: nodeRef,
|
// node: nodeRef,
|
||||||
// connection: connectionRef,
|
// connection: connectionRef,
|
||||||
// key: key of warning as string}
|
// key: key of warning as string}
|
||||||
private getWarningsForRef(ref, create?) {
|
private getWarningsForRef(ref: WarningRef, create?) {
|
||||||
var componentName = ref.component ? ref.component.name : '/';
|
const componentName = ref.component ? ref.component.name : '/';
|
||||||
if (!this.warnings[componentName]) this.warnings[componentName] = {};
|
if (!this.warnings[componentName]) this.warnings[componentName] = {};
|
||||||
var cw = this.warnings[componentName];
|
const cw = this.warnings[componentName];
|
||||||
|
|
||||||
var key;
|
let key;
|
||||||
if (ref.node) key = 'node/' + ref.node.id;
|
if (ref.node) key = 'node/' + ref.node.id;
|
||||||
else if (ref.connection)
|
else if (ref.connection)
|
||||||
key =
|
key =
|
||||||
@@ -220,7 +257,8 @@ export class WarningsModel extends Model {
|
|||||||
|
|
||||||
/** Batch changed notifications so listeners don't get peppered */
|
/** Batch changed notifications so listeners don't get peppered */
|
||||||
private scheduleNotifyChanged() {
|
private scheduleNotifyChanged() {
|
||||||
var _this = this;
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const _this = this;
|
||||||
|
|
||||||
if (this.notifyChangedScheduled) return;
|
if (this.notifyChangedScheduled) return;
|
||||||
this.notifyChangedScheduled = true;
|
this.notifyChangedScheduled = true;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { NodeGraphContextProvider } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
import { NodeGraphContextProvider } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
||||||
|
import { NodeReferencesContextProvider } from '@noodl-contexts/NodeReferencesContext';
|
||||||
|
import { PluginContextProvider } from '@noodl-contexts/PluginContext';
|
||||||
import { ProjectDesignTokenContextProvider } from '@noodl-contexts/ProjectDesignTokenContext';
|
import { ProjectDesignTokenContextProvider } from '@noodl-contexts/ProjectDesignTokenContext';
|
||||||
import { useKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
|
import { useKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
|
||||||
import { useModel } from '@noodl-hooks/useModel';
|
import { useModel } from '@noodl-hooks/useModel';
|
||||||
@@ -43,7 +45,6 @@ import { BaseWindow } from '../../views/windows/BaseWindow';
|
|||||||
import { whatsnewRender } from '../../whats-new';
|
import { whatsnewRender } from '../../whats-new';
|
||||||
import { IRouteProps } from '../AppRoute';
|
import { IRouteProps } from '../AppRoute';
|
||||||
import { useSetupSettings } from './useSetupSettings';
|
import { useSetupSettings } from './useSetupSettings';
|
||||||
import { PluginContextProvider } from '@noodl-contexts/PluginContext';
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const ImportOverwritePopupTemplate = require('../../templates/importoverwritepopup.html');
|
const ImportOverwritePopupTemplate = require('../../templates/importoverwritepopup.html');
|
||||||
@@ -223,28 +224,30 @@ export function EditorPage({ route }: EditorPageProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeGraphContextProvider>
|
<NodeGraphContextProvider>
|
||||||
<ProjectDesignTokenContextProvider>
|
<NodeReferencesContextProvider>
|
||||||
<PluginContextProvider>
|
<ProjectDesignTokenContextProvider>
|
||||||
<BaseWindow>
|
<PluginContextProvider>
|
||||||
{isLoading ? (
|
<BaseWindow>
|
||||||
<ActivityIndicator />
|
{isLoading ? (
|
||||||
) : (
|
<ActivityIndicator />
|
||||||
<>
|
) : (
|
||||||
<FrameDivider
|
<>
|
||||||
first={<SidePanel />}
|
<FrameDivider
|
||||||
second={<ErrorBoundary>{Boolean(Document) && <Document />}</ErrorBoundary>}
|
first={<SidePanel />}
|
||||||
sizeMin={200}
|
second={<ErrorBoundary>{Boolean(Document) && <Document />}</ErrorBoundary>}
|
||||||
size={frameDividerSize}
|
sizeMin={200}
|
||||||
horizontal
|
size={frameDividerSize}
|
||||||
onSizeChanged={setFrameDividerSize}
|
horizontal
|
||||||
/>
|
onSizeChanged={setFrameDividerSize}
|
||||||
|
/>
|
||||||
|
|
||||||
{Boolean(lesson) && <Frame instance={lesson} isContentSize isFitWidth />}
|
{Boolean(lesson) && <Frame instance={lesson} isContentSize isFitWidth />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</BaseWindow>
|
</BaseWindow>
|
||||||
</PluginContextProvider>
|
</PluginContextProvider>
|
||||||
</ProjectDesignTokenContextProvider>
|
</ProjectDesignTokenContextProvider>
|
||||||
|
</NodeReferencesContextProvider>
|
||||||
</NodeGraphContextProvider>
|
</NodeGraphContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { projectFromDirectory, unzipIntoDirectory } from '../models/projectmodel
|
|||||||
import FileSystem from './filesystem';
|
import FileSystem from './filesystem';
|
||||||
import { tracker } from './tracker';
|
import { tracker } from './tracker';
|
||||||
import { guid } from './utils';
|
import { guid } from './utils';
|
||||||
|
import { getTopLevelWorkingDirectory } from '@noodl/git/src/core/open';
|
||||||
|
|
||||||
export interface ProjectItem {
|
export interface ProjectItem {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -267,12 +268,15 @@ export class LocalProjectsModel extends Model {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isGitProject(project: ProjectModel): boolean {
|
/**
|
||||||
// TODO: check if there's is git in any parent folder too
|
* Check if this project is in a git repository.
|
||||||
|
*
|
||||||
// Check if the git folder exists.
|
* @param project
|
||||||
const gitPath = filesystem.join(project._retainedProjectDirectory, '.git');
|
* @returns
|
||||||
return filesystem.exists(gitPath);
|
*/
|
||||||
|
async isGitProject(project: ProjectModel): Promise<boolean> {
|
||||||
|
const gitPath = await getTopLevelWorkingDirectory(project._retainedProjectDirectory);
|
||||||
|
return gitPath !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentGlobalGitAuth(projectId: string) {
|
setCurrentGlobalGitAuth(projectId: string) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { clearFolders } from './cleanup';
|
|||||||
|
|
||||||
export async function copyProjectFilesToFolder(projectPath: string, direntry: string): Promise<void> {
|
export async function copyProjectFilesToFolder(projectPath: string, direntry: string): Promise<void> {
|
||||||
// TODO: Load something like .noodlignore file list
|
// TODO: Load something like .noodlignore file list
|
||||||
const ignoreFiles = ['.DS_Store', '.gitignore', '.gitattributes', 'project.json', 'Dockerfile'];
|
const ignoreFiles = ['.DS_Store', '.gitignore', '.gitattributes', 'project.json', 'Dockerfile', 'nodelibrary.json'];
|
||||||
|
|
||||||
// Copy everything from the project folder
|
// Copy everything from the project folder
|
||||||
if (!projectPath) {
|
if (!projectPath) {
|
||||||
|
|||||||
@@ -67,6 +67,14 @@ async function _writeFileToFolder({
|
|||||||
runtimeType
|
runtimeType
|
||||||
}: WriteFileToFolderArgs) {
|
}: WriteFileToFolderArgs) {
|
||||||
const fullPath = filesystem.join(getExternalFolderPath(), runtimeType, url);
|
const fullPath = filesystem.join(getExternalFolderPath(), runtimeType, url);
|
||||||
|
|
||||||
|
if (!filesystem.exists(fullPath)) {
|
||||||
|
// TODO: Save this warning somewhere, usually, this is not an issue though.
|
||||||
|
// This occurred because building in dev mode does not create the source map
|
||||||
|
// files which it expects to copy over.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let content = await filesystem.readFile(fullPath);
|
let content = await filesystem.readFile(fullPath);
|
||||||
let filename = url;
|
let filename = url;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ITemplateProvider, ProgressCallback, TemplateItem, TemplateListFilter }
|
|||||||
*/
|
*/
|
||||||
export class NoodlDocsTemplateProvider implements ITemplateProvider {
|
export class NoodlDocsTemplateProvider implements ITemplateProvider {
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return 'https://docs.noodl.net';
|
return this.getDocsEndpoint() || 'https://docs.fluxscape.io';
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private readonly getDocsEndpoint: () => string) {}
|
constructor(private readonly getDocsEndpoint: () => string) {}
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ export interface ArrayDiff<T> {
|
|||||||
changed: T[];
|
changed: T[];
|
||||||
unchanged: T[];
|
unchanged: T[];
|
||||||
}
|
}
|
||||||
|
export function createEmptyArrayDiff<T>(): ArrayDiff<T> {
|
||||||
|
return {
|
||||||
|
deleted: [],
|
||||||
|
created: [],
|
||||||
|
changed: [],
|
||||||
|
unchanged: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProjectDiffItem {
|
export interface ProjectDiffItem {
|
||||||
graph: TSFixme;
|
graph: TSFixme;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default class SchemaHandler {
|
|||||||
public dbCollections: TSFixme[];
|
public dbCollections: TSFixme[];
|
||||||
public systemCollections: TSFixme[];
|
public systemCollections: TSFixme[];
|
||||||
public configSchema: TSFixme;
|
public configSchema: TSFixme;
|
||||||
|
public parseServerVersion: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
EventDispatcher.instance.on(
|
EventDispatcher.instance.on(
|
||||||
@@ -119,6 +120,20 @@ export default class SchemaHandler {
|
|||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get Parse Server Version & Supported features
|
||||||
|
fetch(environment.url + '/serverInfo', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
"_method": "GET",
|
||||||
|
"_ApplicationId": environment.appId,
|
||||||
|
"_MasterKey": environment.masterKey,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((json) => {
|
||||||
|
this.parseServerVersion = json.parseServerVersion;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -129,10 +144,20 @@ export default class SchemaHandler {
|
|||||||
ProjectModel.instance.setMetaData('dbCollections', this.dbCollections);
|
ProjectModel.instance.setMetaData('dbCollections', this.dbCollections);
|
||||||
ProjectModel.instance.setMetaData('systemCollections', this.systemCollections);
|
ProjectModel.instance.setMetaData('systemCollections', this.systemCollections);
|
||||||
ProjectModel.instance.setMetaData('dbConfigSchema', this.configSchema);
|
ProjectModel.instance.setMetaData('dbConfigSchema', this.configSchema);
|
||||||
|
|
||||||
|
const versionNumbers = this.parseServerVersion?.split(".")
|
||||||
|
if (versionNumbers && versionNumbers.length > 0) {
|
||||||
|
// Let's only save the major version number,
|
||||||
|
// since this will be used to determine which verison of the API to use.
|
||||||
|
ProjectModel.instance.setMetaData('dbVersionMajor', versionNumbers[0]);
|
||||||
|
} else {
|
||||||
|
ProjectModel.instance.setMetaData('dbVersionMajor', undefined);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ProjectModel.instance.setMetaData('dbCollections', undefined);
|
ProjectModel.instance.setMetaData('dbCollections', undefined);
|
||||||
ProjectModel.instance.setMetaData('systemCollections', undefined);
|
ProjectModel.instance.setMetaData('systemCollections', undefined);
|
||||||
ProjectModel.instance.setMetaData('dbConfigSchema', undefined);
|
ProjectModel.instance.setMetaData('dbConfigSchema', undefined);
|
||||||
|
ProjectModel.instance.setMetaData('dbVersionMajor', undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,32 @@
|
|||||||
const { ProjectModel } = require('../models/projectmodel');
|
import type { ComponentModel } from '@noodl-models/componentmodel';
|
||||||
|
import type { NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
||||||
|
|
||||||
function matchStrings(string1, string2) {
|
import { ProjectModel } from '../models/projectmodel';
|
||||||
|
|
||||||
|
export type SearchResult = {
|
||||||
|
componentTarget: ComponentModel;
|
||||||
|
nodeTarget?: NodeGraphNode;
|
||||||
|
type?: string;
|
||||||
|
userLabel?: string;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function matchStrings(string1: string, string2: string) {
|
||||||
return string1.toLowerCase().indexOf(string2.toLowerCase()) !== -1;
|
return string1.toLowerCase().indexOf(string2.toLowerCase()) !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchInNodeRecursive(node, searchTerms, component) {
|
function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, component: ComponentModel) {
|
||||||
var results = [];
|
let results: SearchResult[] = [];
|
||||||
var matchLabel = null;
|
let matchLabel: string | null = null;
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
if (node._label !== undefined && matchStrings(node._label, searchTerms)) {
|
if (node._label !== undefined && matchStrings(node._label, searchTerms)) {
|
||||||
matchLabel = node.label;
|
matchLabel = node.label;
|
||||||
} else if (matchStrings(node.id, searchTerms)) {
|
} else if (node.id === searchTerms) {
|
||||||
matchLabel = node.id;
|
matchLabel = node.id;
|
||||||
} else if (matchStrings(node.type.displayName || node.type.name, searchTerms)) {
|
} else if (matchStrings(node.type.displayName || node.type.name, searchTerms)) {
|
||||||
matchLabel = node.label;
|
matchLabel = node.label;
|
||||||
} else {
|
} else {
|
||||||
let parameterNames = Object.keys(node.parameters);
|
const parameterNames = Object.keys(node.parameters);
|
||||||
for (const parameterNameIndex in parameterNames) {
|
for (const parameterNameIndex in parameterNames) {
|
||||||
const parameterName = parameterNames[parameterNameIndex];
|
const parameterName = parameterNames[parameterNameIndex];
|
||||||
|
|
||||||
@@ -25,7 +35,7 @@ function searchInNodeRecursive(node, searchTerms, component) {
|
|||||||
matchStrings(node.parameters[parameterName], searchTerms)
|
matchStrings(node.parameters[parameterName], searchTerms)
|
||||||
) {
|
) {
|
||||||
let displayLabel = parameterName;
|
let displayLabel = parameterName;
|
||||||
let connectionPort = node.type.ports?.find((port) => port.name === parameterName);
|
const connectionPort = node.type.ports?.find((port) => port.name === parameterName);
|
||||||
if (connectionPort) {
|
if (connectionPort) {
|
||||||
displayLabel = connectionPort.displayName;
|
displayLabel = connectionPort.displayName;
|
||||||
}
|
}
|
||||||
@@ -51,9 +61,9 @@ function searchInNodeRecursive(node, searchTerms, component) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (matchLabel === null) {
|
if (matchLabel === null) {
|
||||||
var ports = node.dynamicports;
|
const ports = node.dynamicports;
|
||||||
for (i = 0; i < ports.length; ++i) {
|
for (let i = 0; i < ports.length; ++i) {
|
||||||
var port = ports[i];
|
const port = ports[i];
|
||||||
if (matchStrings(port.name, searchTerms)) {
|
if (matchStrings(port.name, searchTerms)) {
|
||||||
matchLabel = node.label + ' : ' + port.name;
|
matchLabel = node.label + ' : ' + port.name;
|
||||||
break;
|
break;
|
||||||
@@ -62,9 +72,9 @@ function searchInNodeRecursive(node, searchTerms, component) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (matchLabel === null) {
|
if (matchLabel === null) {
|
||||||
var ports = node.ports;
|
const ports = node.ports;
|
||||||
for (i = 0; i < ports.length; ++i) {
|
for (let i = 0; i < ports.length; ++i) {
|
||||||
var port = ports[i];
|
const port = ports[i];
|
||||||
if (matchStrings(port.name, searchTerms)) {
|
if (matchStrings(port.name, searchTerms)) {
|
||||||
matchLabel = node.label + ' : ' + port.name;
|
matchLabel = node.label + ' : ' + port.name;
|
||||||
break;
|
break;
|
||||||
@@ -83,17 +93,17 @@ function searchInNodeRecursive(node, searchTerms, component) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < node.children.length; ++i) {
|
for (let i = 0; i < node.children.length; ++i) {
|
||||||
var child = node.children[i];
|
const child = node.children[i];
|
||||||
var childResults = searchInNodeRecursive(child, searchTerms, component);
|
const childResults = searchInNodeRecursive(child, searchTerms, component);
|
||||||
results = results.concat(childResults);
|
results = results.concat(childResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchInComponent(component, searchTerms) {
|
function searchInComponent(component: ComponentModel, searchTerms: string) {
|
||||||
var results = [];
|
let results: SearchResult[] = [];
|
||||||
if (matchStrings(component.displayName, searchTerms)) {
|
if (matchStrings(component.displayName, searchTerms)) {
|
||||||
results.push({
|
results.push({
|
||||||
componentTarget: component,
|
componentTarget: component,
|
||||||
@@ -102,14 +112,14 @@ function searchInComponent(component, searchTerms) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < component.graph.roots.length; ++i) {
|
for (let i = 0; i < component.graph.roots.length; ++i) {
|
||||||
var node = component.graph.roots[i];
|
const node = component.graph.roots[i];
|
||||||
var nodeResults = searchInNodeRecursive(node, searchTerms, component);
|
const nodeResults = searchInNodeRecursive(node, searchTerms, component);
|
||||||
results = results.concat(nodeResults);
|
results = results.concat(nodeResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.graph.commentsModel.comments) {
|
if (component.graph.commentsModel.comments) {
|
||||||
for (var i = 0; i < component.graph.commentsModel.comments.length; ++i) {
|
for (let i = 0; i < component.graph.commentsModel.comments.length; ++i) {
|
||||||
const comment = component.graph.commentsModel.comments[i];
|
const comment = component.graph.commentsModel.comments[i];
|
||||||
if (matchStrings(comment.text, searchTerms)) {
|
if (matchStrings(comment.text, searchTerms)) {
|
||||||
results.push({
|
results.push({
|
||||||
@@ -132,17 +142,17 @@ function searchInComponent(component, searchTerms) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function performSearch(searchTerms) {
|
export function performSearch(searchTerms: string) {
|
||||||
var results = [];
|
const results: ReturnType<typeof searchInComponent>[] = [];
|
||||||
var root = ProjectModel.instance.getRootNode();
|
const root = ProjectModel.instance.getRootNode();
|
||||||
if (root === undefined) return;
|
if (root === undefined) return;
|
||||||
|
|
||||||
var components = ProjectModel.instance.components;
|
const components = ProjectModel.instance.components;
|
||||||
|
|
||||||
for (var i = 0; i < components.length; ++i) {
|
for (let i = 0; i < components.length; ++i) {
|
||||||
var component = components[i];
|
const component = components[i];
|
||||||
|
|
||||||
var componentResults = searchInComponent(component, searchTerms);
|
const componentResults = searchInComponent(component, searchTerms);
|
||||||
if (componentResults !== null) {
|
if (componentResults !== null) {
|
||||||
//limit the label length (it can search in markdown, css, etc)
|
//limit the label length (it can search in markdown, css, etc)
|
||||||
for (const result of componentResults.results) {
|
for (const result of componentResults.results) {
|
||||||
@@ -211,7 +211,7 @@ export default function Clippy() {
|
|||||||
aiAssistantModel.removeActivity(id);
|
aiAssistantModel.removeActivity(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialPlaceholder = isInputOpen ? 'Select (or type) a command below' : 'Ask Noodl AI';
|
const initialPlaceholder = isInputOpen ? 'Select (or type) a command below' : 'Ask FluxScape AI';
|
||||||
const isPromptInWrongOrder = Boolean(!selectedOption) && Boolean(secondInputValue);
|
const isPromptInWrongOrder = Boolean(!selectedOption) && Boolean(secondInputValue);
|
||||||
const isFullBeta = ['full-beta', 'enterprise'].includes(version);
|
const isFullBeta = ['full-beta', 'enterprise'].includes(version);
|
||||||
const isLimitedBeta = false; // TODO: version === 'limited-beta';
|
const isLimitedBeta = false; // TODO: version === 'limited-beta';
|
||||||
@@ -412,8 +412,8 @@ export default function Clippy() {
|
|||||||
<Text hasBottomSpacing>4. Click the "Verify API Key" button</Text>
|
<Text hasBottomSpacing>4. Click the "Verify API Key" button</Text>
|
||||||
|
|
||||||
<Text hasBottomSpacing>
|
<Text hasBottomSpacing>
|
||||||
If you dont have an API key with GPT-4 access, you can set the Noodl AI to use the Limited Beta in the
|
If you dont have an API key with GPT-4 access, you can set the FluxScape AI to use the Limited Beta in
|
||||||
editor settings.
|
the editor settings.
|
||||||
</Text>
|
</Text>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
size={PrimaryButtonSize.Small}
|
size={PrimaryButtonSize.Small}
|
||||||
@@ -597,8 +597,8 @@ export default function Clippy() {
|
|||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
<Text hasBottomSpacing size={TextSize.Medium}>
|
<Text hasBottomSpacing size={TextSize.Medium}>
|
||||||
You are running the limited beta of Noodl AI. If features fewer commands and a less capable AI. Get full
|
You are running the limited beta of FluxScape AI. If features fewer commands and a less capable AI. Get
|
||||||
beta access by bringing your own GPT-4 API key.
|
full beta access by bringing your own GPT-4 API key.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { usePluginContext } from '@noodl-contexts/PluginContext';
|
import React, { RefObject } from 'react';
|
||||||
import React, { RefObject, useEffect, useRef } from 'react';
|
import { platform } from '@noodl/platform';
|
||||||
|
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
|
|
||||||
import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator';
|
import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator';
|
||||||
import { BaseDialog } from '@noodl-core-ui/components/layout/BaseDialog';
|
import { BaseDialog } from '@noodl-core-ui/components/layout/BaseDialog';
|
||||||
@@ -54,6 +56,7 @@ export function DeployPopup(props: DeployPopupProps) {
|
|||||||
onClose={props.onClose}
|
onClose={props.onClose}
|
||||||
hasArrow
|
hasArrow
|
||||||
isLockingScroll
|
isLockingScroll
|
||||||
|
alwaysMounted
|
||||||
>
|
>
|
||||||
<DeployPopupChild />
|
<DeployPopupChild />
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
@@ -62,6 +65,21 @@ export function DeployPopup(props: DeployPopupProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FluxscapeDeployTab() {
|
function FluxscapeDeployTab() {
|
||||||
// Preview URL: 'http://192.168.0.33:8574/'
|
const params = {
|
||||||
return <iframe src="https://portal.fluxscape.io" style={{ width: "100%", height: "50vh", borderStyle: "none" }} />;
|
version: platform.getVersion()
|
||||||
|
};
|
||||||
|
|
||||||
|
const projectId = ProjectModel.instance.id;
|
||||||
|
if (projectId) {
|
||||||
|
params['projectId'] = projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(params);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<iframe
|
||||||
|
src={`https://portal.fluxscape.io/?${urlParams.toString()}`}
|
||||||
|
style={{ width: '100%', height: '50vh', borderStyle: 'none' }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { CloudService } from '@noodl-models/CloudServices';
|
|||||||
import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator';
|
import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator';
|
||||||
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
||||||
import { IconButton } from '@noodl-core-ui/components/inputs/IconButton';
|
import { IconButton } from '@noodl-core-ui/components/inputs/IconButton';
|
||||||
import { PrimaryButton } from '@noodl-core-ui/components/inputs/PrimaryButton';
|
|
||||||
import { Box } from '@noodl-core-ui/components/layout/Box';
|
|
||||||
import { ConditionalContainer } from '@noodl-core-ui/components/layout/ConditionalContainer';
|
import { ConditionalContainer } from '@noodl-core-ui/components/layout/ConditionalContainer';
|
||||||
import { Container } from '@noodl-core-ui/components/layout/Container';
|
import { Container } from '@noodl-core-ui/components/layout/Container';
|
||||||
import { VStack } from '@noodl-core-ui/components/layout/Stack';
|
import { VStack } from '@noodl-core-ui/components/layout/Stack';
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function OpenAiSection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CollapsableSection title="Noodl AI (Beta)">
|
<CollapsableSection title="FluxScape AI (Beta)">
|
||||||
<Box hasXSpacing>
|
<Box hasXSpacing>
|
||||||
<VStack>
|
<VStack>
|
||||||
<PropertyPanelRow label="Version" isChanged={false}>
|
<PropertyPanelRow label="Version" isChanged={false}>
|
||||||
@@ -66,7 +66,7 @@ export function OpenAiSection() {
|
|||||||
|
|
||||||
{enabledState === 'disabled' && (
|
{enabledState === 'disabled' && (
|
||||||
<Box hasYSpacing>
|
<Box hasYSpacing>
|
||||||
<Text>Noodl AI is currently disabled.</Text>
|
<Text>FluxScape AI is currently disabled.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -157,16 +157,16 @@ export function OpenAiSection() {
|
|||||||
UNSAFE_style={{ borderRadius: '2px', background: 'var(--theme-color-bg-3)' }}
|
UNSAFE_style={{ borderRadius: '2px', background: 'var(--theme-color-bg-3)' }}
|
||||||
>
|
>
|
||||||
<Title size={TitleSize.Medium} hasBottomSpacing>
|
<Title size={TitleSize.Medium} hasBottomSpacing>
|
||||||
Noodl AI docs
|
FluxScape AI docs
|
||||||
</Title>
|
</Title>
|
||||||
<Text hasBottomSpacing>See setup instructions and guides for how to use Noodl AI on our docs.</Text>
|
<Text hasBottomSpacing>See setup instructions and guides for how to use FluxScape AI on our docs.</Text>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
variant={PrimaryButtonVariant.Muted}
|
variant={PrimaryButtonVariant.Muted}
|
||||||
size={PrimaryButtonSize.Small}
|
size={PrimaryButtonSize.Small}
|
||||||
isGrowing
|
isGrowing
|
||||||
label="Open docs"
|
label="Open docs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
platform.openExternal('https://docs.noodl.net/#/docs/getting-started/noodl-ai/');
|
platform.openExternal('https://docs.fluxscape.io/docs/getting-started/noodl-ai');
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { NodeGraphContextTmp } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
import { NodeGraphContextTmp } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
||||||
|
import { type NodeReference, useNodeReferencesContext } from '@noodl-contexts/NodeReferencesContext';
|
||||||
import { useFocusRefOnPanelActive } from '@noodl-hooks/useFocusRefOnPanelActive';
|
import { useFocusRefOnPanelActive } from '@noodl-hooks/useFocusRefOnPanelActive';
|
||||||
import { useNodeLibraryLoaded } from '@noodl-hooks/useNodeLibraryLoaded';
|
import { useNodeLibraryLoaded } from '@noodl-hooks/useNodeLibraryLoaded';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { INodeColorScheme } from '@noodl-types/nodeTypes';
|
import { INodeColorScheme } from '@noodl-types/nodeTypes';
|
||||||
import { ComponentModel } from '@noodl-models/componentmodel';
|
import { NodeLibrary } from '@noodl-models/nodelibrary';
|
||||||
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
|
||||||
import { NodeLibrary, NodeLibraryNodeType } from '@noodl-models/nodelibrary';
|
|
||||||
import { BasicNodeType } from '@noodl-models/nodelibrary/BasicNodeType';
|
import { BasicNodeType } from '@noodl-models/nodelibrary/BasicNodeType';
|
||||||
import { ProjectModel } from '@noodl-models/projectmodel';
|
|
||||||
|
|
||||||
import { EditorNode } from '@noodl-core-ui/components/common/EditorNode';
|
import { EditorNode } from '@noodl-core-ui/components/common/EditorNode';
|
||||||
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
||||||
@@ -26,113 +24,17 @@ import { Section, SectionVariant } from '@noodl-core-ui/components/sidebar/Secti
|
|||||||
import { Label } from '@noodl-core-ui/components/typography/Label';
|
import { Label } from '@noodl-core-ui/components/typography/Label';
|
||||||
|
|
||||||
import { NodeReferencesPanel_ID } from '.';
|
import { NodeReferencesPanel_ID } from '.';
|
||||||
import { EventDispatcher } from '../../../../../shared/utils/EventDispatcher';
|
|
||||||
|
|
||||||
type ResultItem = {
|
|
||||||
type: NodeLibraryNodeType;
|
|
||||||
displayName: string;
|
|
||||||
referenaces: {
|
|
||||||
displayName: string;
|
|
||||||
node?: NodeGraphNode;
|
|
||||||
component: ComponentModel;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
function useNodeReferences() {
|
|
||||||
const [group] = useState({});
|
|
||||||
const [result, setResult] = useState<ResultItem[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function updateIndex() {
|
|
||||||
const types: { [key: string]: ResultItem['type'] } = {};
|
|
||||||
const references = new Map<string, ResultItem['referenaces']>();
|
|
||||||
|
|
||||||
function handleComponent(component: ComponentModel) {
|
|
||||||
component.forEachNode((node: NodeGraphNode) => {
|
|
||||||
const name = node.type.name;
|
|
||||||
|
|
||||||
// Add the reference
|
|
||||||
references.set(name, [
|
|
||||||
...(references.get(name) || []),
|
|
||||||
{
|
|
||||||
displayName: component.displayName || component.name,
|
|
||||||
node,
|
|
||||||
component
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Repeater
|
|
||||||
if (name === 'For Each' && node.parameters.template) {
|
|
||||||
const templateComponent = ProjectModel.instance.getComponentWithName(node.parameters.template);
|
|
||||||
|
|
||||||
if (templateComponent) {
|
|
||||||
references.set(templateComponent.fullName, [
|
|
||||||
...(references.get(templateComponent.fullName) || []),
|
|
||||||
{
|
|
||||||
displayName: component.displayName || component.name,
|
|
||||||
node,
|
|
||||||
component
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
handleComponent(templateComponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add some metadata for this node if we dont have it yet.
|
|
||||||
if (!types[name]) {
|
|
||||||
types[name] = node.type;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop all the nodes in the project
|
|
||||||
ProjectModel.instance.forEachComponent(handleComponent);
|
|
||||||
|
|
||||||
// Combine the result to look a little better.
|
|
||||||
const results: ResultItem[] = Array.from(references.keys())
|
|
||||||
.map((key) => ({
|
|
||||||
type: types[key],
|
|
||||||
displayName: types[key]?.displayName || key,
|
|
||||||
referenaces: references.get(key)
|
|
||||||
}))
|
|
||||||
.sort((a, b) => b.referenaces.length - a.referenaces.length);
|
|
||||||
|
|
||||||
setResult(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateIndex();
|
|
||||||
|
|
||||||
EventDispatcher.instance.on(
|
|
||||||
[
|
|
||||||
'Model.nodeAdded',
|
|
||||||
'Model.nodeRemoved',
|
|
||||||
'Model.componentAdded',
|
|
||||||
'Model.componentRemoved',
|
|
||||||
'Model.componentRenamed'
|
|
||||||
],
|
|
||||||
updateIndex,
|
|
||||||
group
|
|
||||||
);
|
|
||||||
|
|
||||||
return function () {
|
|
||||||
EventDispatcher.instance.off(group);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return [result];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NodeReferencesPanel() {
|
export function NodeReferencesPanel() {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [includeCoreNodes, setIncludeCoreNodes] = useState(false);
|
const [includeCoreNodes, setIncludeCoreNodes] = useState(false);
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [result] = useNodeReferences();
|
const { nodeReferences } = useNodeReferencesContext();
|
||||||
const nodeLibraryLoaded = useNodeLibraryLoaded();
|
const nodeLibraryLoaded = useNodeLibraryLoaded();
|
||||||
|
|
||||||
useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID);
|
useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID);
|
||||||
|
|
||||||
function searchFilter(x: ResultItem) {
|
function searchFilter(x: NodeReference) {
|
||||||
if (x.displayName.toLowerCase().includes(searchTerm)) {
|
if (x.displayName.toLowerCase().includes(searchTerm)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -144,7 +46,7 @@ export function NodeReferencesPanel() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filteredResult = result.filter(searchFilter);
|
let filteredResult = nodeReferences.filter(searchFilter);
|
||||||
if (!includeCoreNodes) {
|
if (!includeCoreNodes) {
|
||||||
filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/'));
|
filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/'));
|
||||||
}
|
}
|
||||||
@@ -185,7 +87,7 @@ export function NodeReferencesPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ItemProps {
|
interface ItemProps {
|
||||||
entry: ResultItem;
|
entry: NodeReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Item({ entry }: ItemProps) {
|
function Item({ entry }: ItemProps) {
|
||||||
@@ -245,8 +147,8 @@ function Item({ entry }: ItemProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ItemReferenceProps {
|
interface ItemReferenceProps {
|
||||||
entry: ResultItem;
|
entry: NodeReference;
|
||||||
referenace: ResultItem['referenaces'][0];
|
referenace: NodeReference['referenaces'][0];
|
||||||
colors: INodeColorScheme;
|
colors: INodeColorScheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ function BaseVersionControlPanel() {
|
|||||||
|
|
||||||
export function VersionControlPanel() {
|
export function VersionControlPanel() {
|
||||||
const [git, setGit] = useState<Git>(null);
|
const [git, setGit] = useState<Git>(null);
|
||||||
|
const [state, setState] = useState<'loading' | 'loaded' | 'not-git'>('loading');
|
||||||
|
|
||||||
async function createGit() {
|
async function createGit() {
|
||||||
const gitClient = new Git(mergeProject);
|
const gitClient = new Git(mergeProject);
|
||||||
@@ -206,12 +207,18 @@ export function VersionControlPanel() {
|
|||||||
setGit(gitClient);
|
setGit(gitClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isGitProject = git === null ? LocalProjectsModel.instance.isGitProject(ProjectModel.instance) : true;
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isGitProject) {
|
LocalProjectsModel.instance
|
||||||
createGit();
|
.isGitProject(ProjectModel.instance)
|
||||||
}
|
.then(async (isGitProject) => {
|
||||||
}, [isGitProject]);
|
if (isGitProject) {
|
||||||
|
await createGit();
|
||||||
|
setState('loaded');
|
||||||
|
} else {
|
||||||
|
setState('not-git');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
async function setupGit() {
|
async function setupGit() {
|
||||||
const gitClient = new Git(mergeProject);
|
const gitClient = new Git(mergeProject);
|
||||||
@@ -220,7 +227,7 @@ export function VersionControlPanel() {
|
|||||||
setGit(gitClient);
|
setGit(gitClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (git === null && !isGitProject) {
|
if (git === null && state === 'not-git') {
|
||||||
return (
|
return (
|
||||||
<BasePanel isFill title="Version Control">
|
<BasePanel isFill title="Version Control">
|
||||||
<Box hasXSpacing hasYSpacing>
|
<Box hasXSpacing hasYSpacing>
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import { Commit } from '@noodl/git/src/core/models/snapshot';
|
|||||||
import { FileChange } from '@noodl/git/src/core/models/status';
|
import { FileChange } from '@noodl/git/src/core/models/status';
|
||||||
import { revRange } from '@noodl/git/src/core/rev-list';
|
import { revRange } from '@noodl/git/src/core/rev-list';
|
||||||
|
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
|
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
|
||||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||||
import { ProjectDiff, diffProject } from '@noodl-utils/projectmerger.diff';
|
import { ProjectDiff, diffProject } from '@noodl-utils/projectmerger.diff';
|
||||||
|
|
||||||
import { useVersionControlContext } from '../context';
|
import { useVersionControlContext } from '../context';
|
||||||
|
import { getProjectFilePath } from '../context/DiffUtils';
|
||||||
import { DiffList } from './DiffList';
|
import { DiffList } from './DiffList';
|
||||||
|
|
||||||
//Kind:
|
//Kind:
|
||||||
@@ -124,7 +126,10 @@ async function getMergeDiff(repositoryPath: string, commit: Commit, refToDiffTo:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getProjectFile(commit: Commit) {
|
async function getProjectFile(commit: Commit) {
|
||||||
const projectContent = JSON.parse(await commit.getFileAsString('project.json'));
|
const projectFilePath = getProjectFilePath(commit.repositoryDir, ProjectModel.instance._retainedProjectDirectory);
|
||||||
|
|
||||||
|
const projectContentRaw = await commit.getFileAsString(projectFilePath);
|
||||||
|
const projectContent = JSON.parse(projectContentRaw);
|
||||||
applyPatches(projectContent);
|
applyPatches(projectContent);
|
||||||
return projectContent;
|
return projectContent;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export function DiffList({ diff, fileChanges, componentDiffTitle, actions, commi
|
|||||||
const [imageDiff, setImageDiff] = useState<IImageDiff>(null);
|
const [imageDiff, setImageDiff] = useState<IImageDiff>(null);
|
||||||
|
|
||||||
const components = diff?.components ? getChangedComponents(diff.components) : [];
|
const components = diff?.components ? getChangedComponents(diff.components) : [];
|
||||||
const files = (fileChanges || [])?.filter((f) => f.path !== 'project.json') || [];
|
const files = (fileChanges || [])?.filter((f) => !f.path.endsWith('project.json')) || [];
|
||||||
const settings = diff?.settings ? getChangedObjectProperties(diff.settings) : [];
|
const settings = diff?.settings ? getChangedObjectProperties(diff.settings) : [];
|
||||||
const colorStyles = diff?.styles.colors ? getChangedObjectProperties(diff.styles.colors) : [];
|
const colorStyles = diff?.styles.colors ? getChangedObjectProperties(diff.styles.colors) : [];
|
||||||
const textStyles = diff?.styles.text ? getChangedObjectProperties(diff.styles.text) : [];
|
const textStyles = diff?.styles.text ? getChangedObjectProperties(diff.styles.text) : [];
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import { getCommit } from '@noodl/git/src/core/logs';
|
||||||
|
import { FileStatusKind } from '@noodl/git/src/core/models/status';
|
||||||
|
|
||||||
|
import { FeedbackType } from '@noodl-constants/FeedbackType';
|
||||||
|
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
|
||||||
import {
|
import {
|
||||||
ProjectDiff,
|
ProjectDiff,
|
||||||
ProjectDiffItem,
|
ProjectDiffItem,
|
||||||
ProjectBasicDiffItem,
|
ProjectBasicDiffItem,
|
||||||
ArrayDiff,
|
ArrayDiff,
|
||||||
diffProject
|
diffProject,
|
||||||
|
createEmptyArrayDiff
|
||||||
} from '@noodl-utils/projectmerger.diff';
|
} from '@noodl-utils/projectmerger.diff';
|
||||||
|
|
||||||
|
import { IconName } from '@noodl-core-ui/components/common/Icon';
|
||||||
|
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
|
||||||
|
|
||||||
import { ProjectModel } from '../../../../models/projectmodel';
|
import { ProjectModel } from '../../../../models/projectmodel';
|
||||||
|
|
||||||
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
|
export interface ProjectLocalDiff extends ProjectDiff {
|
||||||
import { FileStatusKind } from '@noodl/git/src/core/models/status';
|
|
||||||
import { IconName } from '@noodl-core-ui/components/common/Icon';
|
|
||||||
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
|
|
||||||
import { FeedbackType } from '@noodl-constants/FeedbackType';
|
|
||||||
import { getCommit } from '@noodl/git/src/core/logs';
|
|
||||||
|
|
||||||
export interface ProjectLocalDiff extends ProjectDiff{
|
|
||||||
baseProject: TSFixme; //Project model as an object from raw json
|
baseProject: TSFixme; //Project model as an object from raw json
|
||||||
commitShaDiffedTo: string;
|
commitShaDiffedTo: string;
|
||||||
}
|
}
|
||||||
@@ -84,17 +87,49 @@ export function getFileStatusIconProps(status: FileStatusKind): Partial<ListItem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doLocalDiff(repositoryPath: string, headCommitId: string): Promise<ProjectLocalDiff> {
|
export function getProjectFilePath(repositoryPath: string, projectPath: string) {
|
||||||
const baseCommit = await getCommit(repositoryPath, headCommitId);
|
const relativePath = path.relative(repositoryPath, projectPath);
|
||||||
const baseProjectJson = await baseCommit.getFileAsString('project.json');
|
const projectFilePath = path.join(relativePath, 'project.json').replaceAll('\\', '/');
|
||||||
const baseProject = JSON.parse(baseProjectJson);
|
return projectFilePath;
|
||||||
applyPatches(baseProject);
|
}
|
||||||
|
|
||||||
const diff = diffProject(baseProject, ProjectModel.instance.toJSON());
|
export async function doLocalDiff(
|
||||||
|
repositoryPath: string,
|
||||||
return {
|
projectPath: string,
|
||||||
...diff,
|
headCommitId: string
|
||||||
baseProject,
|
): Promise<ProjectLocalDiff> {
|
||||||
commitShaDiffedTo: headCommitId
|
const projectFilePath = getProjectFilePath(repositoryPath, projectPath);
|
||||||
};
|
|
||||||
|
try {
|
||||||
|
const baseCommit = await getCommit(projectPath, headCommitId);
|
||||||
|
const baseProjectJson = await baseCommit.getFileAsString(projectFilePath);
|
||||||
|
const baseProject = JSON.parse(baseProjectJson);
|
||||||
|
applyPatches(baseProject);
|
||||||
|
|
||||||
|
const diff = diffProject(baseProject, ProjectModel.instance.toJSON());
|
||||||
|
|
||||||
|
return {
|
||||||
|
...diff,
|
||||||
|
baseProject,
|
||||||
|
commitShaDiffedTo: headCommitId
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (error.toString().includes('exists on disk, but not in')) {
|
||||||
|
console.warn('project.json does not exist in this commit.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return empty state
|
||||||
|
return {
|
||||||
|
baseProject: {},
|
||||||
|
commitShaDiffedTo: headCommitId,
|
||||||
|
components: createEmptyArrayDiff(),
|
||||||
|
variants: createEmptyArrayDiff(),
|
||||||
|
settings: createEmptyArrayDiff(),
|
||||||
|
styles: {
|
||||||
|
colors: createEmptyArrayDiff(),
|
||||||
|
text: createEmptyArrayDiff()
|
||||||
|
},
|
||||||
|
cloudservices: createEmptyArrayDiff()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Slot } from '@noodl-core-ui/types/global';
|
|||||||
import { doLocalDiff, ProjectLocalDiff } from './DiffUtils';
|
import { doLocalDiff, ProjectLocalDiff } from './DiffUtils';
|
||||||
import { useVersionControlFetch } from './fetch.context';
|
import { useVersionControlFetch } from './fetch.context';
|
||||||
import { BranchStatus, IVersionControlContext } from './types';
|
import { BranchStatus, IVersionControlContext } from './types';
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
|
|
||||||
const VersionControlContext = createContext<IVersionControlContext>({
|
const VersionControlContext = createContext<IVersionControlContext>({
|
||||||
git: null,
|
git: null,
|
||||||
@@ -57,7 +58,8 @@ export function VersionControlProvider({ git, children }: { git: Git; children:
|
|||||||
(async () => {
|
(async () => {
|
||||||
const currentCommitSha = await git.getHeadCommitId();
|
const currentCommitSha = await git.getHeadCommitId();
|
||||||
if (currentCommitSha) {
|
if (currentCommitSha) {
|
||||||
const diff = await doLocalDiff(git.repositoryPath, currentCommitSha);
|
const projectPath = ProjectModel.instance._retainedProjectDirectory;
|
||||||
|
const diff = await doLocalDiff(git.repositoryPath, projectPath, currentCommitSha);
|
||||||
setLocalDiff(diff);
|
setLocalDiff(diff);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ import { EventDispatcher } from '../../../../../shared/utils/EventDispatcher';
|
|||||||
import View from '../../../../../shared/view';
|
import View from '../../../../../shared/view';
|
||||||
import { NodeGraphEditor } from '../../nodegrapheditor';
|
import { NodeGraphEditor } from '../../nodegrapheditor';
|
||||||
import * as NewPopupLayer from '../../PopupLayer/index';
|
import * as NewPopupLayer from '../../PopupLayer/index';
|
||||||
|
import { type PopupMenuItem } from '../../PopupLayer/index';
|
||||||
import { ToastLayer } from '../../ToastLayer/ToastLayer';
|
import { ToastLayer } from '../../ToastLayer/ToastLayer';
|
||||||
import { ComponentsPanelFolder } from './ComponentsPanelFolder';
|
import { ComponentsPanelFolder } from './ComponentsPanelFolder';
|
||||||
import { ComponentTemplates } from './ComponentTemplates';
|
import { ComponentTemplates } from './ComponentTemplates';
|
||||||
|
import { HACK_findNodeReference } from '@noodl-contexts/NodeReferencesContext';
|
||||||
|
|
||||||
const PopupLayer = require('@noodl-views/popuplayer');
|
const PopupLayer = require('@noodl-views/popuplayer');
|
||||||
const ComponentsPanelTemplate = require('../../../templates/componentspanel.html');
|
const ComponentsPanelTemplate = require('../../../templates/componentspanel.html');
|
||||||
@@ -961,7 +963,7 @@ export class ComponentsPanelView extends View {
|
|||||||
forRuntimeType: this.getRuntimeType()
|
forRuntimeType: this.getRuntimeType()
|
||||||
});
|
});
|
||||||
|
|
||||||
let items: TSFixme[] = templates.map((t) => ({
|
let items: PopupMenuItem[] = templates.map((t) => ({
|
||||||
icon: IconName.Plus,
|
icon: IconName.Plus,
|
||||||
label: t.label,
|
label: t.label,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -987,6 +989,10 @@ export class ComponentsPanelView extends View {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find references
|
||||||
|
const nodeReference = HACK_findNodeReference(scope.comp.name);
|
||||||
|
const nodeReferencesText = `Used in ~${nodeReference?.referenaces?.length || 0} places`;
|
||||||
|
|
||||||
items = items.concat([
|
items = items.concat([
|
||||||
{
|
{
|
||||||
icon: IconName.Pencil,
|
icon: IconName.Pencil,
|
||||||
@@ -1011,6 +1017,9 @@ export class ComponentsPanelView extends View {
|
|||||||
_this.onDeleteClicked(scope, el);
|
_this.onDeleteClicked(scope, el);
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: nodeReferencesText
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -1110,6 +1119,16 @@ export class ComponentsPanelView extends View {
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (scope.canBecomeRoot) {
|
||||||
|
// Find references
|
||||||
|
const nodeReference = HACK_findNodeReference(scope.folder.component.name);
|
||||||
|
const nodeReferencesText = `Used in ~${nodeReference?.referenaces?.length || 0} places`;
|
||||||
|
|
||||||
|
items = items.concat([{
|
||||||
|
label: nodeReferencesText
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
const menu = new NewPopupLayer.PopupMenu({
|
const menu = new NewPopupLayer.PopupMenu({
|
||||||
items: items
|
items: items
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ function AiNodeChat({ context, onUpdated }: AiNodeChatProps) {
|
|||||||
footer={
|
footer={
|
||||||
version === 'disabled' ? (
|
version === 'disabled' ? (
|
||||||
<Center>
|
<Center>
|
||||||
<Text textType={TextType.Shy}>Noodl AI is currently disabled.</Text>
|
<Text textType={TextType.Shy}>FluxScape AI is currently disabled.</Text>
|
||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import classNames from 'classnames';
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
|
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
|
||||||
import { performSearch } from '@noodl-utils/universal-search';
|
import { performSearch, SearchResult } from '@noodl-utils/universal-search';
|
||||||
|
|
||||||
import { SearchInput } from '@noodl-core-ui/components/inputs/SearchInput';
|
import { SearchInput } from '@noodl-core-ui/components/inputs/SearchInput';
|
||||||
import { Container } from '@noodl-core-ui/components/layout/Container';
|
import { Container } from '@noodl-core-ui/components/layout/Container';
|
||||||
@@ -19,7 +19,7 @@ import css from './search-panel.module.scss';
|
|||||||
|
|
||||||
export function SearchPanel() {
|
export function SearchPanel() {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [searchResults, setSearchResults] = useState([]);
|
const [searchResults, setSearchResults] = useState<ReturnType<typeof performSearch>>([]);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
// TODO: Not same context
|
// TODO: Not same context
|
||||||
@@ -54,7 +54,7 @@ export function SearchPanel() {
|
|||||||
}
|
}
|
||||||
}, [debouncedSearchTerm]);
|
}, [debouncedSearchTerm]);
|
||||||
|
|
||||||
function onSearchItemClicked(searchResult) {
|
function onSearchItemClicked(searchResult: SearchResult) {
|
||||||
if (searchResult.type === 'Component') {
|
if (searchResult.type === 'Component') {
|
||||||
NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, {
|
NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, {
|
||||||
breadcrumbs: false,
|
breadcrumbs: false,
|
||||||
@@ -85,29 +85,7 @@ export function SearchPanel() {
|
|||||||
|
|
||||||
<div className={css.SearchResults}>
|
<div className={css.SearchResults}>
|
||||||
{searchResults.map((component) => (
|
{searchResults.map((component) => (
|
||||||
<Section
|
<SearchItem key={component.componentId} component={component} onSearchItemClicked={onSearchItemClicked} />
|
||||||
title={`${component.componentName} (${component.results.length} result${
|
|
||||||
component.results.length > 1 ? 's' : ''
|
|
||||||
})`}
|
|
||||||
key={component.componentId}
|
|
||||||
variant={SectionVariant.Panel}
|
|
||||||
>
|
|
||||||
{component.results.map((result, index) => (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
css.SearchResultItem
|
|
||||||
// lastActiveComponentId === result.componentTarget.id && css['is-active']
|
|
||||||
)}
|
|
||||||
key={index}
|
|
||||||
onClick={() => onSearchItemClicked(result)}
|
|
||||||
>
|
|
||||||
<Label variant={TextType.Proud}>
|
|
||||||
{result.userLabel ? result.type + ' - ' + result.userLabel : result.type}
|
|
||||||
</Label>
|
|
||||||
<Text>{result.label}</Text>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Section>
|
|
||||||
))}
|
))}
|
||||||
{searchResults.length === 0 && debouncedSearchTerm.length > 0 && (
|
{searchResults.length === 0 && debouncedSearchTerm.length > 0 && (
|
||||||
<Container hasXSpacing hasYSpacing>
|
<Container hasXSpacing hasYSpacing>
|
||||||
@@ -118,3 +96,39 @@ export function SearchPanel() {
|
|||||||
</BasePanel>
|
</BasePanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchItemProps = {
|
||||||
|
component: ReturnType<typeof performSearch>[0];
|
||||||
|
onSearchItemClicked: (item: SearchResult) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function SearchItem({ component, onSearchItemClicked }: SearchItemProps) {
|
||||||
|
const resultCountText = `${component.results.length} result${component.results.length > 1 ? 's' : ''}`;
|
||||||
|
let titleText = `${component.componentName} (${resultCountText})`;
|
||||||
|
|
||||||
|
// We expect there to always be at least one result, to get the full component name.
|
||||||
|
const isInCloudFunctions = component.results[0].componentTarget.name.startsWith('/#__cloud__/');
|
||||||
|
if (isInCloudFunctions) {
|
||||||
|
titleText += ' (Cloud Function)';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title={titleText} variant={SectionVariant.Panel}>
|
||||||
|
{component.results.map((result, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={classNames(
|
||||||
|
css.SearchResultItem
|
||||||
|
// lastActiveComponentId === result.componentTarget.id && css['is-active']
|
||||||
|
)}
|
||||||
|
onClick={() => onSearchItemClicked(result)}
|
||||||
|
>
|
||||||
|
<Label variant={TextType.Proud}>
|
||||||
|
{result.userLabel ? result.type + ' - ' + result.userLabel : result.type}
|
||||||
|
</Label>
|
||||||
|
<Text>{result.label}</Text>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ export async function open(basePath: string): Promise<string> {
|
|||||||
// console.log("VCS error when opening project: " + e);
|
// console.log("VCS error when opening project: " + e);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return basePath;
|
|
||||||
|
// Find the relative git repository path
|
||||||
|
const repositoryPath = await getTopLevelWorkingDirectory(basePath);
|
||||||
|
|
||||||
|
return repositoryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -32,12 +32,15 @@ class CloudStore {
|
|||||||
|
|
||||||
_initCloudServices() {
|
_initCloudServices() {
|
||||||
_collections = undefined; // clear collection cache, so it's refetched
|
_collections = undefined; // clear collection cache, so it's refetched
|
||||||
const cloudServices = NoodlRuntime.instance.getMetaData('cloudservices');
|
|
||||||
|
|
||||||
|
const cloudServices = NoodlRuntime.instance.getMetaData('cloudservices');
|
||||||
if (cloudServices) {
|
if (cloudServices) {
|
||||||
this.appId = cloudServices.appId;
|
this.appId = cloudServices.appId;
|
||||||
this.endpoint = cloudServices.endpoint;
|
this.endpoint = cloudServices.endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dbVersionMajor = NoodlRuntime.instance.getMetaData('dbVersionMajor');
|
||||||
|
this.dbVersionMajor = dbVersionMajor;
|
||||||
}
|
}
|
||||||
|
|
||||||
on() {
|
on() {
|
||||||
@@ -70,8 +73,9 @@ class CloudStore {
|
|||||||
xhr.open(options.method || 'GET', this.endpoint + path, true);
|
xhr.open(options.method || 'GET', this.endpoint + path, true);
|
||||||
|
|
||||||
xhr.setRequestHeader('X-Parse-Application-Id', this.appId);
|
xhr.setRequestHeader('X-Parse-Application-Id', this.appId);
|
||||||
if (typeof _noodl_cloudservices !== 'undefined')
|
if (typeof _noodl_cloudservices !== 'undefined') {
|
||||||
xhr.setRequestHeader('X-Parse-Master-Key', _noodl_cloudservices.masterKey);
|
xhr.setRequestHeader('X-Parse-Master-Key', _noodl_cloudservices.masterKey);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for current users
|
// Check for current users
|
||||||
var _cu = localStorage['Parse/' + this.appId + '/currentUser'];
|
var _cu = localStorage['Parse/' + this.appId + '/currentUser'];
|
||||||
@@ -168,13 +172,10 @@ class CloudStore {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
|
|
||||||
if (options.limit) args.push('limit=' + options.limit);
|
if (options.limit) args.push('limit=' + options.limit);
|
||||||
if (options.skip) args.push('skip=' + options.skip);
|
if (options.skip) args.push('skip=' + options.skip);
|
||||||
|
|
||||||
const grouping = {
|
const grouping = {};
|
||||||
objectId: null
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(options.group).forEach((k) => {
|
Object.keys(options.group).forEach((k) => {
|
||||||
const _g = {};
|
const _g = {};
|
||||||
@@ -188,7 +189,20 @@ class CloudStore {
|
|||||||
grouping[k] = _g;
|
grouping[k] = _g;
|
||||||
});
|
});
|
||||||
|
|
||||||
args.push('group=' + JSON.stringify(grouping));
|
// I don't know which version the API was changed, lets just say above 4 for now.
|
||||||
|
if (this.dbVersionMajor && this.dbVersionMajor > 4) {
|
||||||
|
grouping._id = null;
|
||||||
|
|
||||||
|
if (options.where) args.push('$match=' + encodeURIComponent(JSON.stringify(options.where)));
|
||||||
|
|
||||||
|
args.push('$group=' + JSON.stringify(grouping));
|
||||||
|
} else {
|
||||||
|
grouping.objectId = null;
|
||||||
|
|
||||||
|
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
|
||||||
|
|
||||||
|
args.push('group=' + JSON.stringify(grouping));
|
||||||
|
}
|
||||||
|
|
||||||
this._makeRequest('/aggregate/' + options.collection + (args.length > 0 ? '?' + args.join('&') : ''), {
|
this._makeRequest('/aggregate/' + options.collection + (args.length > 0 ? '?' + args.join('&') : ''), {
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
@@ -244,11 +258,34 @@ class CloudStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {{
|
||||||
|
* objectId: string;
|
||||||
|
* collection: string;
|
||||||
|
* keys?: string[] | string;
|
||||||
|
* include?: string[] | string;
|
||||||
|
* excludeKeys?: string[] | string;
|
||||||
|
* success: (data: unknown) => void;
|
||||||
|
* error: (error: unknown) => void;
|
||||||
|
* }} options
|
||||||
|
*/
|
||||||
fetch(options) {
|
fetch(options) {
|
||||||
const args = [];
|
const args = [];
|
||||||
|
|
||||||
if (options.include)
|
if (options.include) {
|
||||||
args.push('include=' + (Array.isArray(options.include) ? options.include.join(',') : options.include));
|
args.push('include=' + (Array.isArray(options.include) ? options.include.join(',') : options.include));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.keys) {
|
||||||
|
args.push('keys=' + (Array.isArray(options.keys) ? options.keys.join(',') : options.keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.excludeKeys) {
|
||||||
|
args.push(
|
||||||
|
'excludeKeys=' + (Array.isArray(options.excludeKeys) ? options.excludeKeys.join(',') : options.excludeKeys)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this._makeRequest(
|
this._makeRequest(
|
||||||
'/classes/' + options.collection + '/' + options.objectId + (args.length > 0 ? '?' + args.join('&') : ''),
|
'/classes/' + options.collection + '/' + options.objectId + (args.length > 0 ? '?' + args.join('&') : ''),
|
||||||
@@ -420,6 +457,8 @@ class CloudStore {
|
|||||||
* file: {
|
* file: {
|
||||||
* name: string;
|
* name: string;
|
||||||
* }
|
* }
|
||||||
|
* success: (data: unknown) => void;
|
||||||
|
* error: (error: unknown) => void;
|
||||||
* }} options
|
* }} options
|
||||||
*/
|
*/
|
||||||
deleteFile(options) {
|
deleteFile(options) {
|
||||||
@@ -432,8 +471,15 @@ class CloudStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _isArrayOfObjects(a) {
|
function _isArrayOfObjects(a) {
|
||||||
if (!Array.isArray(a)) return false;
|
if (!Array.isArray(a)) {
|
||||||
for (var i = 0; i < a.length; i++) if (typeof a[i] !== 'object' || a[i] === null) return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
if (typeof a[i] !== 'object' || a[i] === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -505,66 +551,104 @@ function _serializeObject(data, collectionName, modelScope) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {unknown} data
|
||||||
|
* @param {string} type
|
||||||
|
* @param {*} modelScope
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
function _deserializeJSON(data, type, modelScope) {
|
function _deserializeJSON(data, type, modelScope) {
|
||||||
if (data === undefined) return;
|
if (data === undefined) return undefined;
|
||||||
if (data === null) return null;
|
if (data === null) return null;
|
||||||
|
|
||||||
if (type === 'Relation' && data.__type === 'Relation') {
|
if (type === 'Relation' && data.__type === 'Relation') {
|
||||||
return undefined; // Ignore relation fields
|
return undefined; // Ignore relation fields
|
||||||
} else if (type === 'Pointer' && data.__type === 'Pointer') {
|
}
|
||||||
// This is a pointer type, resolve into id
|
|
||||||
|
// This is a pointer type, resolve into id
|
||||||
|
if (type === 'Pointer' && data.__type === 'Pointer') {
|
||||||
return data.objectId;
|
return data.objectId;
|
||||||
} else if (type === 'Date' && data.__type === 'Date') {
|
}
|
||||||
|
|
||||||
|
if (type === 'Date' && data.__type === 'Date') {
|
||||||
return new Date(data.iso);
|
return new Date(data.iso);
|
||||||
} else if (type === 'Date' && typeof data === 'string') {
|
}
|
||||||
|
|
||||||
|
if (type === 'Date' && typeof data === 'string') {
|
||||||
return new Date(data);
|
return new Date(data);
|
||||||
} else if (type === 'File' && data.__type === 'File') {
|
}
|
||||||
|
|
||||||
|
if (type === 'File' && data.__type === 'File') {
|
||||||
return new CloudFile(data);
|
return new CloudFile(data);
|
||||||
} else if (type === 'GeoPoint' && data.__type === 'GeoPoint') {
|
}
|
||||||
|
|
||||||
|
if (type === 'GeoPoint' && data.__type === 'GeoPoint') {
|
||||||
return {
|
return {
|
||||||
latitude: data.latitude,
|
latitude: data.latitude,
|
||||||
longitude: data.longitude
|
longitude: data.longitude
|
||||||
};
|
};
|
||||||
} else if (_isArrayOfObjects(data)) {
|
}
|
||||||
var a = [];
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
if (_isArrayOfObjects(data)) {
|
||||||
|
const a = [];
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
a.push(_deserializeJSON(data[i], undefined, modelScope));
|
a.push(_deserializeJSON(data[i], undefined, modelScope));
|
||||||
}
|
}
|
||||||
var c = Collection.get();
|
const c = Collection.get();
|
||||||
c.set(a);
|
c.set(a);
|
||||||
return c;
|
return c;
|
||||||
} else if (Array.isArray(data)) return data;
|
}
|
||||||
|
|
||||||
|
// An array with mixed types
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
// This is an array with mixed data, just return it
|
// This is an array with mixed data, just return it
|
||||||
else if (data && data.__type === 'Object' && data.className !== undefined && data.objectId !== undefined) {
|
if (data && data.__type === 'Object' && data.className !== undefined && data.objectId !== undefined) {
|
||||||
const _data = Object.assign({}, data);
|
const _data = Object.assign({}, data);
|
||||||
delete _data.className;
|
delete _data.className;
|
||||||
delete _data.__type;
|
delete _data.__type;
|
||||||
return _fromJSON(_data, data.className, modelScope);
|
return _fromJSON(_data, data.className, modelScope);
|
||||||
} else if (typeof data === 'object' && data !== null) {
|
}
|
||||||
var m = (modelScope || Model).get();
|
|
||||||
for (var key in data) {
|
if (typeof data === 'object' && data !== null) {
|
||||||
m.set(key, _deserializeJSON(data[key], undefined, modelScope));
|
// Try to get the model by id, if it is defined, otherwise we create a new unique id.
|
||||||
|
const model = (modelScope || Model).get(data.id);
|
||||||
|
for (const key in data) {
|
||||||
|
const nestedValue = _deserializeJSON(data[key], undefined, modelScope);
|
||||||
|
model.set(key, nestedValue);
|
||||||
}
|
}
|
||||||
return m;
|
return model;
|
||||||
} else return data;
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _fromJSON(item, collectionName, modelScope) {
|
function _fromJSON(item, collectionName, modelScope) {
|
||||||
const m = (modelScope || Model).get(item.objectId);
|
const modelStore = modelScope || Model;
|
||||||
m._class = collectionName;
|
|
||||||
|
|
||||||
if (collectionName !== undefined && CloudStore._collections[collectionName] !== undefined)
|
// Try to get the model by the object id (record) or id, otherwise we create a new unique id.
|
||||||
var schema = CloudStore._collections[collectionName].schema;
|
const model = modelStore.get(item.objectId || item.id);
|
||||||
|
model._class = collectionName;
|
||||||
|
|
||||||
for (var key in item) {
|
let schema = undefined;
|
||||||
if (key === 'objectId' || key === 'ACL') continue;
|
if (collectionName !== undefined && CloudStore._collections[collectionName] !== undefined) {
|
||||||
|
schema = CloudStore._collections[collectionName].schema;
|
||||||
var _type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
|
||||||
|
|
||||||
m.set(key, _deserializeJSON(item[key], _type, modelScope));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m;
|
for (const key in item) {
|
||||||
|
if (key === 'objectId' || key === 'ACL') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
||||||
|
const nestedValue = _deserializeJSON(item[key], _type, modelScope);
|
||||||
|
model.set(key, nestedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloudStore._fromJSON = _fromJSON;
|
CloudStore._fromJSON = _fromJSON;
|
||||||
|
|||||||
@@ -38,12 +38,11 @@ function convertVisualFilter(query, options) {
|
|||||||
var value = query.input !== undefined ? inputs[query.input] : query.value;
|
var value = query.input !== undefined ? inputs[query.input] : query.value;
|
||||||
|
|
||||||
if (query.operator === 'exist') {
|
if (query.operator === 'exist') {
|
||||||
_res[query.property] = { $exists: true };
|
_res[query.property] = { $exists: true };
|
||||||
return _res;
|
return _res;
|
||||||
}
|
} else if (query.operator === 'not exist') {
|
||||||
else if (query.operator === 'not exist') {
|
_res[query.property] = { $exists: false };
|
||||||
_res[query.property] = { $exists: false };
|
return _res;
|
||||||
return _res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === undefined) return;
|
if (value === undefined) return;
|
||||||
@@ -80,7 +79,6 @@ function convertVisualFilter(query, options) {
|
|||||||
cond = { $regex: value, $options: 'i' };
|
cond = { $regex: value, $options: 'i' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_res[query.property] = cond;
|
_res[query.property] = cond;
|
||||||
|
|
||||||
return _res;
|
return _res;
|
||||||
@@ -163,10 +161,22 @@ function _value(v) {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Record<string, unknown>} filter
|
||||||
|
* @param {{
|
||||||
|
* collectionName?: string;
|
||||||
|
* modelScope?: unknown;
|
||||||
|
* error: (error: string) => void;
|
||||||
|
* }} options
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
function convertFilterOp(filter, options) {
|
function convertFilterOp(filter, options) {
|
||||||
const keys = Object.keys(filter);
|
const keys = Object.keys(filter);
|
||||||
if (keys.length === 0) return {};
|
if (keys.length === 0) return {};
|
||||||
if (keys.length !== 1) return options.error('Filter must only have one key found ' + keys.join(','));
|
if (keys.length !== 1) {
|
||||||
|
return options.error('Filter must only have one key found ' + keys.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
const res = {};
|
const res = {};
|
||||||
const key = keys[0];
|
const key = keys[0];
|
||||||
@@ -179,18 +189,27 @@ function convertFilterOp(filter, options) {
|
|||||||
} else if (filter['idContainedIn'] !== undefined) {
|
} else if (filter['idContainedIn'] !== undefined) {
|
||||||
res['objectId'] = { $in: filter['idContainedIn'] };
|
res['objectId'] = { $in: filter['idContainedIn'] };
|
||||||
} else if (filter['relatedTo'] !== undefined) {
|
} else if (filter['relatedTo'] !== undefined) {
|
||||||
var modelId = filter['relatedTo']['id'];
|
const modelId = filter['relatedTo']['id'];
|
||||||
if (modelId === undefined) return options.error('Must provide id in relatedTo filter');
|
if (modelId === undefined) {
|
||||||
|
return options.error('Must provide id in relatedTo filter');
|
||||||
|
}
|
||||||
|
|
||||||
var relationKey = filter['relatedTo']['key'];
|
const relationKey = filter['relatedTo']['key'];
|
||||||
if (relationKey === undefined) return options.error('Must provide key in relatedTo filter');
|
if (relationKey === undefined) {
|
||||||
|
return options.error('Must provide key in relatedTo filter');
|
||||||
|
}
|
||||||
|
|
||||||
|
const className = filter['relatedTo']['className'] || (options.modelScope || Model).get(modelId)?._class;
|
||||||
|
if (typeof className === 'undefined') {
|
||||||
|
// Either the pointer is loaded as an object or we allow passing in the className.
|
||||||
|
return options.error('Must preload the Pointer or include className');
|
||||||
|
}
|
||||||
|
|
||||||
var m = (options.modelScope || Model).get(modelId);
|
|
||||||
res['$relatedTo'] = {
|
res['$relatedTo'] = {
|
||||||
object: {
|
object: {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
objectId: modelId,
|
objectId: modelId,
|
||||||
className: m._class
|
className
|
||||||
},
|
},
|
||||||
key: relationKey
|
key: relationKey
|
||||||
};
|
};
|
||||||
@@ -208,13 +227,14 @@ function convertFilterOp(filter, options) {
|
|||||||
else if (opAndValue['containedIn'] !== undefined) res[key] = { $in: opAndValue['containedIn'] };
|
else if (opAndValue['containedIn'] !== undefined) res[key] = { $in: opAndValue['containedIn'] };
|
||||||
else if (opAndValue['notContainedIn'] !== undefined) res[key] = { $nin: opAndValue['notContainedIn'] };
|
else if (opAndValue['notContainedIn'] !== undefined) res[key] = { $nin: opAndValue['notContainedIn'] };
|
||||||
else if (opAndValue['pointsTo'] !== undefined) {
|
else if (opAndValue['pointsTo'] !== undefined) {
|
||||||
var m = (options.modelScope || Model).get(opAndValue['pointsTo']);
|
let schema = null;
|
||||||
if (CloudStore._collections[options.collectionName])
|
if (CloudStore._collections[options.collectionName]) {
|
||||||
var schema = CloudStore._collections[options.collectionName].schema;
|
schema = CloudStore._collections[options.collectionName].schema;
|
||||||
|
}
|
||||||
|
|
||||||
var targetClass =
|
const targetClass =
|
||||||
schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined;
|
schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined;
|
||||||
var type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
const type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
||||||
|
|
||||||
if (type === 'Relation') {
|
if (type === 'Relation') {
|
||||||
res[key] = {
|
res[key] = {
|
||||||
@@ -223,13 +243,13 @@ function convertFilterOp(filter, options) {
|
|||||||
className: targetClass
|
className: targetClass
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (Array.isArray(opAndValue['pointsTo']))
|
if (Array.isArray(opAndValue['pointsTo'])) {
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$in: opAndValue['pointsTo'].map((v) => {
|
$in: opAndValue['pointsTo'].map((v) => {
|
||||||
return { __type: 'Pointer', objectId: v, className: targetClass };
|
return { __type: 'Pointer', objectId: v, className: targetClass };
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
else
|
} else {
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$eq: {
|
$eq: {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
@@ -237,6 +257,7 @@ function convertFilterOp(filter, options) {
|
|||||||
className: targetClass
|
className: targetClass
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (opAndValue['matchesRegex'] !== undefined) {
|
} else if (opAndValue['matchesRegex'] !== undefined) {
|
||||||
res[key] = {
|
res[key] = {
|
||||||
@@ -257,43 +278,42 @@ function convertFilterOp(filter, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Geo points
|
// Geo points
|
||||||
} else if (opAndValue['nearSphere'] !== undefined) {
|
} else if (opAndValue['nearSphere'] !== undefined) {
|
||||||
var _v = opAndValue['nearSphere'];
|
var _v = opAndValue['nearSphere'];
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$nearSphere: {
|
$nearSphere: {
|
||||||
__type: "GeoPoint",
|
__type: 'GeoPoint',
|
||||||
latitude: _v.latitude,
|
latitude: _v.latitude,
|
||||||
longitude: _v.longitude,
|
longitude: _v.longitude
|
||||||
},
|
},
|
||||||
$maxDistanceInMiles:_v.$maxDistanceInMiles,
|
$maxDistanceInMiles: _v.$maxDistanceInMiles,
|
||||||
$maxDistanceInKilometers:_v.maxDistanceInKilometers,
|
$maxDistanceInKilometers: _v.maxDistanceInKilometers,
|
||||||
$maxDistanceInRadians:_v.maxDistanceInRadians
|
$maxDistanceInRadians: _v.maxDistanceInRadians
|
||||||
};
|
};
|
||||||
} else if (opAndValue['withinBox'] !== undefined) {
|
} else if (opAndValue['withinBox'] !== undefined) {
|
||||||
var _v = opAndValue['withinBox'];
|
var _v = opAndValue['withinBox'];
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$within:{
|
$within: {
|
||||||
$box: _v.map(gp => ({
|
$box: _v.map((gp) => ({
|
||||||
__type:"GeoPoint",
|
__type: 'GeoPoint',
|
||||||
latitude:gp.latitude,
|
latitude: gp.latitude,
|
||||||
longitude:gp.longitude
|
longitude: gp.longitude
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (opAndValue['withinPolygon'] !== undefined) {
|
} else if (opAndValue['withinPolygon'] !== undefined) {
|
||||||
var _v = opAndValue['withinPolygon'];
|
var _v = opAndValue['withinPolygon'];
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$geoWithin:{
|
$geoWithin: {
|
||||||
$polygon: _v.map(gp => ({
|
$polygon: _v.map((gp) => ({
|
||||||
__type:"GeoPoint",
|
__type: 'GeoPoint',
|
||||||
latitude:gp.latitude,
|
latitude: gp.latitude,
|
||||||
longitude:gp.longitude
|
longitude: gp.longitude
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
options.error('Unrecognized filter keys ' + keys.join(','));
|
options.error('Unrecognized filter keys ' + keys.join(','));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
async query(className, query, options) {
|
async query(className, query, options) {
|
||||||
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().query({
|
cloudstore().query({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -26,9 +27,9 @@ function createRecordsAPI(modelScope) {
|
|||||||
include: options ? options.include : undefined,
|
include: options ? options.include : undefined,
|
||||||
select: options ? options.select : undefined,
|
select: options ? options.select : undefined,
|
||||||
count: options ? options.count : undefined,
|
count: options ? options.count : undefined,
|
||||||
success: (results,count) => {
|
success: (results, count) => {
|
||||||
const _results = results.map((r) => cloudstore()._fromJSON(r, className));
|
const _results = results.map((r) => cloudstore()._fromJSON(r, className));
|
||||||
if(count !== undefined) resolve({results:_results,count});
|
if (count !== undefined) resolve({ results: _results, count });
|
||||||
else resolve(_results);
|
else resolve(_results);
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
@@ -39,6 +40,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async count(className, query) {
|
async count(className, query) {
|
||||||
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().count({
|
cloudstore().count({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -60,6 +62,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async distinct(className, property, query) {
|
async distinct(className, property, query) {
|
||||||
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().distinct({
|
cloudstore().distinct({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -82,6 +85,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async aggregate(className, group, query) {
|
async aggregate(className, group, query) {
|
||||||
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().aggregate({
|
cloudstore().aggregate({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -103,19 +107,35 @@ function createRecordsAPI(modelScope) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string | { getId(): string; }} objectOrId
|
||||||
|
* @param {{
|
||||||
|
* className: string;
|
||||||
|
* keys?: string[] | string;
|
||||||
|
* include?: string[] | string;
|
||||||
|
* excludeKeys?: string[] | string;
|
||||||
|
* }} options
|
||||||
|
* @returns {Promise<unknown>}
|
||||||
|
*/
|
||||||
async fetch(objectOrId, options) {
|
async fetch(objectOrId, options) {
|
||||||
|
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
||||||
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
||||||
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!className) return reject('No class name specified');
|
if (!className) {
|
||||||
|
return reject('No class name specified');
|
||||||
|
}
|
||||||
|
|
||||||
cloudstore().fetch({
|
cloudstore().fetch({
|
||||||
collection: className,
|
collection: className,
|
||||||
objectId: objectOrId,
|
objectId: objectOrId,
|
||||||
include: options ? options.include : undefined,
|
keys: options?.keys,
|
||||||
|
include: options?.include,
|
||||||
|
excludeKeys: options?.excludeKeys,
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
var record = cloudstore()._fromJSON(response, className);
|
const record = cloudstore()._fromJSON(response, className);
|
||||||
resolve(record);
|
resolve(record);
|
||||||
},
|
},
|
||||||
error: function (err) {
|
error: function (err) {
|
||||||
@@ -126,6 +146,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async increment(objectOrId, properties, options) {
|
async increment(objectOrId, properties, options) {
|
||||||
|
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
||||||
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
||||||
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
||||||
|
|
||||||
@@ -149,6 +170,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async save(objectOrId, properties, options) {
|
async save(objectOrId, properties, options) {
|
||||||
|
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
||||||
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
||||||
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
||||||
|
|
||||||
@@ -179,6 +201,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async create(className, properties, options) {
|
async create(className, properties, options) {
|
||||||
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().create({
|
cloudstore().create({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -197,6 +220,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async delete(objectOrId, options) {
|
async delete(objectOrId, options) {
|
||||||
|
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
||||||
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
||||||
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
||||||
|
|
||||||
@@ -265,7 +289,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
reject(Error(rr || 'Failed to add relation.'));
|
reject(Error(err || 'Failed to add relation.'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
var Model = require("./model");
|
var Model = require('./model');
|
||||||
|
|
||||||
// Get and set proxy
|
// Get and set proxy
|
||||||
/*const proxies = {}
|
/*const proxies = {}
|
||||||
@@ -221,48 +221,48 @@ Collection.prototype.toJSON = function() {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
Object.defineProperty(Array.prototype, "items", {
|
Object.defineProperty(Array.prototype, 'items', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
get() {
|
get() {
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
set(data) {
|
set(data) {
|
||||||
this.set(data);
|
this.set(data);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
Object.defineProperty(Array.prototype, "each", {
|
Object.defineProperty(Array.prototype, 'each', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: Array.prototype.forEach,
|
value: Array.prototype.forEach
|
||||||
});
|
});
|
||||||
Object.defineProperty(Array.prototype, "size", {
|
Object.defineProperty(Array.prototype, 'size', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function () {
|
value: function () {
|
||||||
return this.length;
|
return this.length;
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
Object.defineProperty(Array.prototype, "get", {
|
Object.defineProperty(Array.prototype, 'get', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function (index) {
|
value: function (index) {
|
||||||
return this[index];
|
return this[index];
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
Object.defineProperty(Array.prototype, "getId", {
|
Object.defineProperty(Array.prototype, 'getId', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function () {
|
value: function () {
|
||||||
return this._id;
|
return this._id;
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
Object.defineProperty(Array.prototype, "id", {
|
Object.defineProperty(Array.prototype, 'id', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
get() {
|
get() {
|
||||||
return this.getId();
|
return this.getId();
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
Object.defineProperty(Array.prototype, "set", {
|
Object.defineProperty(Array.prototype, 'set', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function (src) {
|
value: function (src) {
|
||||||
@@ -323,10 +323,10 @@ Object.defineProperty(Array.prototype, "set", {
|
|||||||
for (i = aItems.length; i < bItems.length; i++) {
|
for (i = aItems.length; i < bItems.length; i++) {
|
||||||
this.add(bItems[i]);
|
this.add(bItems[i]);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Array.prototype, "notify", {
|
Object.defineProperty(Array.prototype, 'notify', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: async function (event, args) {
|
value: async function (event, args) {
|
||||||
@@ -337,80 +337,80 @@ Object.defineProperty(Array.prototype, "notify", {
|
|||||||
for (var i = 0; i < l.length; i++) {
|
for (var i = 0; i < l.length; i++) {
|
||||||
await l[i](args);
|
await l[i](args);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Array.prototype, "contains", {
|
Object.defineProperty(Array.prototype, 'contains', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function (item) {
|
value: function (item) {
|
||||||
return this.indexOf(item) !== -1;
|
return this.indexOf(item) !== -1;
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Array.prototype, "add", {
|
Object.defineProperty(Array.prototype, 'add', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: async function (item) {
|
value: async function (item) {
|
||||||
if (this.contains(item)) return; // Already contains item
|
if (this.contains(item)) return; // Already contains item
|
||||||
|
|
||||||
this.items.push(item);
|
this.items.push(item);
|
||||||
await this.notify("add", { item: item, index: this.items.length - 1 });
|
await this.notify('add', { item: item, index: this.items.length - 1 });
|
||||||
await this.notify("change");
|
await this.notify('change');
|
||||||
await item.notify("add", { collection: this });
|
await item.notify('add', { collection: this });
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Array.prototype, "remove", {
|
Object.defineProperty(Array.prototype, 'remove', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function (item) {
|
value: function (item) {
|
||||||
var idx = this.items.indexOf(item);
|
var idx = this.items.indexOf(item);
|
||||||
if (idx !== -1) this.removeAtIndex(idx);
|
if (idx !== -1) this.removeAtIndex(idx);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Array.prototype, "addAtIndex", {
|
Object.defineProperty(Array.prototype, 'addAtIndex', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: async function (item, index) {
|
value: async function (item, index) {
|
||||||
if (this.contains(item)) return; // Already contains item
|
if (this.contains(item)) return; // Already contains item
|
||||||
|
|
||||||
this.items.splice(index, 0, item);
|
this.items.splice(index, 0, item);
|
||||||
await this.notify("add", { item: item, index: index });
|
await this.notify('add', { item: item, index: index });
|
||||||
await this.notify("change");
|
await this.notify('change');
|
||||||
await item.notify("add", { collection: this, index: index });
|
await item.notify('add', { collection: this, index: index });
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Array.prototype, "removeAtIndex", {
|
Object.defineProperty(Array.prototype, 'removeAtIndex', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: async function (idx) {
|
value: async function (idx) {
|
||||||
var item = this.items[idx];
|
var item = this.items[idx];
|
||||||
this.items.splice(idx, 1);
|
this.items.splice(idx, 1);
|
||||||
await this.notify("remove", { item: item, index: idx });
|
await this.notify('remove', { item: item, index: idx });
|
||||||
await this.notify("change");
|
await this.notify('change');
|
||||||
await item.notify("remove", { collection: this });
|
await item.notify('remove', { collection: this });
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Array.prototype, "on", {
|
Object.defineProperty(Array.prototype, 'on', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function (event, listener) {
|
value: function (event, listener) {
|
||||||
if (!this._listeners)
|
if (!this._listeners)
|
||||||
Object.defineProperty(this, "_listeners", {
|
Object.defineProperty(this, '_listeners', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: {},
|
value: {}
|
||||||
});
|
});
|
||||||
if (!this._listeners[event]) this._listeners[event] = [];
|
if (!this._listeners[event]) this._listeners[event] = [];
|
||||||
this._listeners[event].push(listener);
|
this._listeners[event].push(listener);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Array.prototype, "off", {
|
Object.defineProperty(Array.prototype, 'off', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function (event, listener) {
|
value: function (event, listener) {
|
||||||
@@ -418,20 +418,20 @@ Object.defineProperty(Array.prototype, "off", {
|
|||||||
if (!this._listeners[event]) return;
|
if (!this._listeners[event]) return;
|
||||||
var idx = this._listeners[event].indexOf(listener);
|
var idx = this._listeners[event].indexOf(listener);
|
||||||
if (idx !== -1) this._listeners[event].splice(idx, 1);
|
if (idx !== -1) this._listeners[event].splice(idx, 1);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class Collection extends Array {}
|
class Collection extends Array {}
|
||||||
|
|
||||||
var collections = (Collection._collections = {});
|
const collections = (Collection._collections = {});
|
||||||
|
|
||||||
Collection.create = function (items) {
|
Collection.create = function (items) {
|
||||||
const name = Model.guid();
|
const name = Model.guid();
|
||||||
collections[name] = new Collection();
|
collections[name] = new Collection();
|
||||||
Object.defineProperty(collections[name], "_id", {
|
Object.defineProperty(collections[name], '_id', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: name,
|
value: name
|
||||||
});
|
});
|
||||||
if (items) {
|
if (items) {
|
||||||
collections[name].set(items);
|
collections[name].set(items);
|
||||||
@@ -439,14 +439,18 @@ Collection.create = function (items) {
|
|||||||
return collections[name];
|
return collections[name];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {Collection}
|
||||||
|
*/
|
||||||
Collection.get = function (name) {
|
Collection.get = function (name) {
|
||||||
if (name === undefined) name = Model.guid();
|
if (name === undefined) name = Model.guid();
|
||||||
if (!collections[name]) {
|
if (!collections[name]) {
|
||||||
collections[name] = new Collection();
|
collections[name] = new Collection();
|
||||||
Object.defineProperty(collections[name], "_id", {
|
Object.defineProperty(collections[name], '_id', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: name,
|
value: name
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ const _modelProxyHandler = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} id
|
||||||
|
* @returns {Model}
|
||||||
|
*/
|
||||||
Model.get = function (id) {
|
Model.get = function (id) {
|
||||||
if (id === undefined) id = Model.guid();
|
if (id === undefined) id = Model.guid();
|
||||||
if (!models[id]) {
|
if (!models[id]) {
|
||||||
@@ -122,13 +127,22 @@ Model.prototype.fill = function (value = null) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {unknown} value
|
||||||
|
* @param {{
|
||||||
|
* resolve?: boolean;
|
||||||
|
* forceChange?: boolean;
|
||||||
|
* silent?: boolean;
|
||||||
|
* }} args
|
||||||
|
*/
|
||||||
Model.prototype.set = function (name, value, args) {
|
Model.prototype.set = function (name, value, args) {
|
||||||
if (args && args.resolve && name.indexOf('.') !== -1) {
|
if (args && args.resolve && name.indexOf('.') !== -1) {
|
||||||
// We should resolve path references
|
// We should resolve path references
|
||||||
var path = name.split('.');
|
const path = name.split('.');
|
||||||
var model = this;
|
let model = this;
|
||||||
for (var i = 0; i < path.length - 1; i++) {
|
for (let i = 0; i < path.length - 1; i++) {
|
||||||
var v = model.get(path[i]);
|
const v = model.get(path[i]);
|
||||||
if (Model.instanceOf(v)) model = v;
|
if (Model.instanceOf(v)) model = v;
|
||||||
else return; // Path resolve failed
|
else return; // Path resolve failed
|
||||||
}
|
}
|
||||||
@@ -138,24 +152,35 @@ Model.prototype.set = function (name, value, args) {
|
|||||||
|
|
||||||
const forceChange = args && args.forceChange;
|
const forceChange = args && args.forceChange;
|
||||||
|
|
||||||
var oldValue = this.data[name];
|
const oldValue = this.data[name];
|
||||||
this.data[name] = value;
|
this.data[name] = value;
|
||||||
(forceChange || oldValue !== value) &&
|
|
||||||
(!args || !args.silent) &&
|
if ((forceChange || oldValue !== value) && (!args || !args.silent)) {
|
||||||
this.notify('change', { name: name, value: value, old: oldValue });
|
this.notify('change', { name: name, value: value, old: oldValue });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
Model.prototype.getId = function () {
|
Model.prototype.getId = function () {
|
||||||
return this.id;
|
return this.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {{
|
||||||
|
* resolve?: boolean;
|
||||||
|
* }} args
|
||||||
|
* @returns {unknown}
|
||||||
|
*/
|
||||||
Model.prototype.get = function (name, args) {
|
Model.prototype.get = function (name, args) {
|
||||||
if (args && args.resolve && name.indexOf('.') !== -1) {
|
if (args && args.resolve && name.indexOf('.') !== -1) {
|
||||||
// We should resolve path references
|
// We should resolve path references
|
||||||
var path = name.split('.');
|
const path = name.split('.');
|
||||||
var model = this;
|
let model = this;
|
||||||
for (var i = 0; i < path.length - 1; i++) {
|
for (let i = 0; i < path.length - 1; i++) {
|
||||||
var v = model.get(path[i]);
|
const v = model.get(path[i]);
|
||||||
if (Model.instanceOf(v)) model = v;
|
if (Model.instanceOf(v)) model = v;
|
||||||
else return; // Path resolve failed
|
else return; // Path resolve failed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ NodeContext.prototype.deregisterComponentModel = function (componentModel) {
|
|||||||
|
|
||||||
NodeContext.prototype.fetchComponentBundle = async function (name) {
|
NodeContext.prototype.fetchComponentBundle = async function (name) {
|
||||||
const fetchBundle = async (name) => {
|
const fetchBundle = async (name) => {
|
||||||
let baseUrl = Noodl.Env["BaseUrl"] || '/';
|
let baseUrl = Noodl.Env['BaseUrl'] || '/';
|
||||||
let bundleUrl = `${baseUrl}noodl_bundles/${name}.json`;
|
let bundleUrl = `${baseUrl}noodl_bundles/${name}.json`;
|
||||||
|
|
||||||
const response = await fetch(bundleUrl);
|
const response = await fetch(bundleUrl);
|
||||||
@@ -455,6 +455,15 @@ NodeContext.prototype.setPopupCallbacks = function ({ onShow, onClose }) {
|
|||||||
this.onClosePopup = onClose;
|
this.onClosePopup = onClose;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} popupComponent
|
||||||
|
* @param {Record<string, unknown>} params
|
||||||
|
* @param {{
|
||||||
|
* senderNode?: unknown;
|
||||||
|
* onClosePopup?: (action?: string, results: object) => void;
|
||||||
|
* }} args
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
NodeContext.prototype.showPopup = async function (popupComponent, params, args) {
|
NodeContext.prototype.showPopup = async function (popupComponent, params, args) {
|
||||||
if (!this.onShowPopup) return;
|
if (!this.onShowPopup) return;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var DbCollectionNode = {
|
|||||||
_this.scheduleAfterInputsHaveUpdated(function () {
|
_this.scheduleAfterInputsHaveUpdated(function () {
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
_this.flagOutputDirty('firstItemId');
|
_this.flagOutputDirty('firstItemId');
|
||||||
|
_this.flagOutputDirty('isEmpty');
|
||||||
collectionChangedScheduled = false;
|
collectionChangedScheduled = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -66,6 +67,7 @@ var DbCollectionNode = {
|
|||||||
|
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
_this.flagOutputDirty('firstItemId');
|
_this.flagOutputDirty('firstItemId');
|
||||||
|
_this.flagOutputDirty('isEmpty');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.type === 'create') {
|
if (args.type === 'create') {
|
||||||
@@ -91,6 +93,7 @@ var DbCollectionNode = {
|
|||||||
|
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
_this.flagOutputDirty('firstItemId');
|
_this.flagOutputDirty('firstItemId');
|
||||||
|
_this.flagOutputDirty('isEmpty');
|
||||||
} else if (matchesQuery && !_this._internal.collection.contains(m)) {
|
} else if (matchesQuery && !_this._internal.collection.contains(m)) {
|
||||||
// It's not part of the result collection but now matches they query, add it and resort
|
// It's not part of the result collection but now matches they query, add it and resort
|
||||||
_addModelAtCorrectIndex(m);
|
_addModelAtCorrectIndex(m);
|
||||||
@@ -106,6 +109,7 @@ var DbCollectionNode = {
|
|||||||
|
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
_this.flagOutputDirty('firstItemId');
|
_this.flagOutputDirty('firstItemId');
|
||||||
|
_this.flagOutputDirty('isEmpty');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -153,6 +157,17 @@ var DbCollectionNode = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isEmpty: {
|
||||||
|
type: 'boolean',
|
||||||
|
displayName: 'Is Empty',
|
||||||
|
group: 'General',
|
||||||
|
getter: function () {
|
||||||
|
if (this._internal.collection) {
|
||||||
|
return this._internal.collection.size() === 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
count: {
|
count: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
displayName: 'Count',
|
displayName: 'Count',
|
||||||
@@ -189,6 +204,7 @@ var DbCollectionNode = {
|
|||||||
setCollection: function (collection) {
|
setCollection: function (collection) {
|
||||||
this.bindCollection(collection);
|
this.bindCollection(collection);
|
||||||
this.flagOutputDirty('firstItemId');
|
this.flagOutputDirty('firstItemId');
|
||||||
|
this.flagOutputDirty('isEmpty');
|
||||||
this.flagOutputDirty('items');
|
this.flagOutputDirty('items');
|
||||||
this.flagOutputDirty('count');
|
this.flagOutputDirty('count');
|
||||||
},
|
},
|
||||||
@@ -257,7 +273,7 @@ var DbCollectionNode = {
|
|||||||
limit: limit,
|
limit: limit,
|
||||||
skip: skip,
|
skip: skip,
|
||||||
count: count,
|
count: count,
|
||||||
success: (results,count) => {
|
success: (results, count) => {
|
||||||
if (results !== undefined) {
|
if (results !== undefined) {
|
||||||
_c.set(
|
_c.set(
|
||||||
results.map((i) => {
|
results.map((i) => {
|
||||||
@@ -267,10 +283,9 @@ var DbCollectionNode = {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if(count !== undefined) {
|
if (count !== undefined) {
|
||||||
this._internal.storageSettings.storageTotalCount = count;
|
this._internal.storageSettings.storageTotalCount = count;
|
||||||
if(this.hasOutput('storageTotalCount'))
|
if (this.hasOutput('storageTotalCount')) this.flagOutputDirty('storageTotalCount');
|
||||||
this.flagOutputDirty('storageTotalCount');
|
|
||||||
}
|
}
|
||||||
this.setCollection(_c);
|
this.setCollection(_c);
|
||||||
this.sendSignalOnOutput('fetched');
|
this.sendSignalOnOutput('fetched');
|
||||||
@@ -383,7 +398,7 @@ var DbCollectionNode = {
|
|||||||
if (!storageSettings['storageEnableLimit']) return;
|
if (!storageSettings['storageEnableLimit']) return;
|
||||||
else return storageSettings['storageSkip'] || 0;
|
else return storageSettings['storageSkip'] || 0;
|
||||||
},
|
},
|
||||||
getStorageFetchTotalCount: function() {
|
getStorageFetchTotalCount: function () {
|
||||||
const storageSettings = this._internal.storageSettings;
|
const storageSettings = this._internal.storageSettings;
|
||||||
|
|
||||||
return !!storageSettings['storageEnableCount'];
|
return !!storageSettings['storageEnableCount'];
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
const { Node, EdgeTriggeredInput } = require('../../../../noodl-runtime');
|
const { Node, EdgeTriggeredInput } = require('../../../../noodl-runtime');
|
||||||
|
|
||||||
var Model = require('../../../model');
|
const Model = require('../../../model');
|
||||||
const CloudStore = require('../../../api/cloudstore');
|
const CloudStore = require('../../../api/cloudstore');
|
||||||
|
|
||||||
var ModelNodeDefinition = {
|
const ModelNodeDefinition = {
|
||||||
name: 'DbModel2',
|
name: 'DbModel2',
|
||||||
docs: 'https://docs.noodl.net/nodes/data/cloud-data/record',
|
docs: 'https://docs.noodl.net/nodes/data/cloud-data/record',
|
||||||
displayNodeName: 'Record',
|
displayNodeName: 'Record',
|
||||||
@@ -21,11 +21,11 @@ var ModelNodeDefinition = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
var internal = this._internal;
|
const internal = this._internal;
|
||||||
internal.inputValues = {};
|
internal.inputValues = {};
|
||||||
internal.relationModelIds = {};
|
internal.relationModelIds = {};
|
||||||
|
|
||||||
var _this = this;
|
const _this = this;
|
||||||
this._internal.onModelChangedCallback = function (args) {
|
this._internal.onModelChangedCallback = function (args) {
|
||||||
if (_this.isInputConnected('fetch')) return;
|
if (_this.isInputConnected('fetch')) return;
|
||||||
|
|
||||||
@@ -109,13 +109,18 @@ var ModelNodeDefinition = {
|
|||||||
displayName: 'Id',
|
displayName: 'Id',
|
||||||
group: 'General',
|
group: 'General',
|
||||||
set: function (value) {
|
set: function (value) {
|
||||||
if (value instanceof Model) value = value.getId();
|
if (value instanceof Model) {
|
||||||
// Can be passed as model as well
|
// Can be passed as model as well
|
||||||
else if (typeof value === 'object') value = Model.create(value).getId(); // If this is an js object, dereference it
|
value = value.getId();
|
||||||
|
} else if (typeof value === 'object') {
|
||||||
|
// If this is an js object, dereference it
|
||||||
|
value = Model.create(value).getId();
|
||||||
|
}
|
||||||
|
|
||||||
this._internal.modelId = value; // Wait to fetch data
|
this._internal.modelId = value; // Wait to fetch data
|
||||||
if (this.isInputConnected('fetch') === false) this.setModelID(value);
|
if (this.isInputConnected('fetch') === false) {
|
||||||
else {
|
this.setModelID(value);
|
||||||
|
} else {
|
||||||
this.flagOutputDirty('id');
|
this.flagOutputDirty('id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,9 +143,10 @@ var ModelNodeDefinition = {
|
|||||||
this.setModel(model);
|
this.setModel(model);
|
||||||
},
|
},
|
||||||
setModel: function (model) {
|
setModel: function (model) {
|
||||||
if (this._internal.model)
|
if (this._internal.model) {
|
||||||
// Remove old listener if existing
|
// Remove old listener if existing
|
||||||
this._internal.model.off('change', this._internal.onModelChangedCallback);
|
this._internal.model.off('change', this._internal.onModelChangedCallback);
|
||||||
|
}
|
||||||
|
|
||||||
this._internal.model = model;
|
this._internal.model = model;
|
||||||
this.flagOutputDirty('id');
|
this.flagOutputDirty('id');
|
||||||
@@ -148,7 +154,9 @@ var ModelNodeDefinition = {
|
|||||||
|
|
||||||
// We have a new model, mark all outputs as dirty
|
// We have a new model, mark all outputs as dirty
|
||||||
for (var key in model.data) {
|
for (var key in model.data) {
|
||||||
if (this.hasOutput('prop-' + key)) this.flagOutputDirty('prop-' + key);
|
if (this.hasOutput('prop-' + key)) {
|
||||||
|
this.flagOutputDirty('prop-' + key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.sendSignalOnOutput('fetched');
|
this.sendSignalOnOutput('fetched');
|
||||||
},
|
},
|
||||||
@@ -184,7 +192,7 @@ var ModelNodeDefinition = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
scheduleFetch: function () {
|
scheduleFetch: function () {
|
||||||
var _this = this;
|
const _this = this;
|
||||||
const internal = this._internal;
|
const internal = this._internal;
|
||||||
|
|
||||||
this.scheduleOnce('Fetch', function () {
|
this.scheduleOnce('Fetch', function () {
|
||||||
@@ -199,12 +207,13 @@ var ModelNodeDefinition = {
|
|||||||
collection: internal.collectionId,
|
collection: internal.collectionId,
|
||||||
objectId: internal.modelId, // Get the objectId part of the model id
|
objectId: internal.modelId, // Get the objectId part of the model id
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
var model = cloudstore._fromJSON(response, internal.collectionId);
|
const model = cloudstore._fromJSON(response, internal.collectionId);
|
||||||
if (internal.model !== model) {
|
if (internal.model !== model) {
|
||||||
// Check if we need to change model
|
// Check if we need to change model
|
||||||
if (internal.model)
|
if (internal.model) {
|
||||||
// Remove old listener if existing
|
// Remove old listener if existing
|
||||||
internal.model.off('change', internal.onModelChangedCallback);
|
internal.model.off('change', internal.onModelChangedCallback);
|
||||||
|
}
|
||||||
|
|
||||||
internal.model = model;
|
internal.model = model;
|
||||||
model.on('change', internal.onModelChangedCallback);
|
model.on('change', internal.onModelChangedCallback);
|
||||||
@@ -213,8 +222,10 @@ var ModelNodeDefinition = {
|
|||||||
|
|
||||||
delete response.objectId;
|
delete response.objectId;
|
||||||
|
|
||||||
for (var key in response) {
|
for (const key in response) {
|
||||||
if (_this.hasOutput('prop-' + key)) _this.flagOutputDirty('prop-' + key);
|
if (_this.hasOutput('prop-' + key)) {
|
||||||
|
_this.flagOutputDirty('prop-' + key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_this.sendSignalOnOutput('fetched');
|
_this.sendSignalOnOutput('fetched');
|
||||||
@@ -226,7 +237,6 @@ var ModelNodeDefinition = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
scheduleStore: function () {
|
scheduleStore: function () {
|
||||||
var _this = this;
|
|
||||||
var internal = this._internal;
|
var internal = this._internal;
|
||||||
if (!internal.model) return;
|
if (!internal.model) return;
|
||||||
|
|
||||||
@@ -247,8 +257,6 @@ var ModelNodeDefinition = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
registerInputIfNeeded: function (name) {
|
registerInputIfNeeded: function (name) {
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
if (this.hasInput(name)) {
|
if (this.hasInput(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -328,8 +336,7 @@ function updatePorts(nodeId, parameters, editorConnection, graphModel) {
|
|||||||
var p = props[key];
|
var p = props[key];
|
||||||
if (ports.find((_p) => _p.name === key)) continue;
|
if (ports.find((_p) => _p.name === key)) continue;
|
||||||
|
|
||||||
if (p.type === 'Relation') {
|
if (p.type !== 'Relation') {
|
||||||
} else {
|
|
||||||
// Other schema type ports
|
// Other schema type ports
|
||||||
const _typeMap = {
|
const _typeMap = {
|
||||||
String: 'string',
|
String: 'string',
|
||||||
@@ -373,16 +380,16 @@ module.exports = {
|
|||||||
function _managePortsForNode(node) {
|
function _managePortsForNode(node) {
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
||||||
|
|
||||||
node.on('parameterUpdated', function (event) {
|
node.on('parameterUpdated', function () {
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
graphModel.on('metadataChanged.dbCollections', function (data) {
|
graphModel.on('metadataChanged.dbCollections', function () {
|
||||||
CloudStore.invalidateCollections();
|
CloudStore.invalidateCollections();
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
graphModel.on('metadataChanged.systemCollections', function (data) {
|
graphModel.on('metadataChanged.systemCollections', function () {
|
||||||
CloudStore.invalidateCollections();
|
CloudStore.invalidateCollections();
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ const DateToStringNode = {
|
|||||||
|
|
||||||
this._internal.currentInput = _value;
|
this._internal.currentInput = _value;
|
||||||
this._format();
|
this._format();
|
||||||
this.flagOutputDirty('currentValue');
|
|
||||||
this.sendSignalOnOutput('inputChanged');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -49,30 +47,45 @@ const DateToStringNode = {
|
|||||||
type: 'signal',
|
type: 'signal',
|
||||||
displayName: 'Date Changed',
|
displayName: 'Date Changed',
|
||||||
group: 'Signals'
|
group: 'Signals'
|
||||||
|
},
|
||||||
|
onError: {
|
||||||
|
type: 'signal',
|
||||||
|
displayName: 'Invalid Date',
|
||||||
|
group: 'Signals'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
_format() {
|
_format() {
|
||||||
const t = this._internal.currentInput;
|
try {
|
||||||
const format = this._internal.formatString;
|
const t = this._internal.currentInput;
|
||||||
const date = ('0' + t.getDate()).slice(-2);
|
const format = this._internal.formatString;
|
||||||
const month = ('0' + (t.getMonth() + 1)).slice(-2);
|
const date = ('0' + t.getDate()).slice(-2);
|
||||||
const monthShort = new Intl.DateTimeFormat('en-US', { month: 'short' }).format(t);
|
const month = ('0' + (t.getMonth() + 1)).slice(-2);
|
||||||
const year = t.getFullYear();
|
const monthShort = new Intl.DateTimeFormat('en-US', { month: 'short' }).format(t);
|
||||||
const yearShort = year.toString().substring(2);
|
const year = t.getFullYear();
|
||||||
const hours = ('0' + t.getHours()).slice(-2);
|
const yearShort = year.toString().substring(2);
|
||||||
const minutes = ('0' + t.getMinutes()).slice(-2);
|
const hours = ('0' + t.getHours()).slice(-2);
|
||||||
const seconds = ('0' + t.getSeconds()).slice(-2);
|
const minutes = ('0' + t.getMinutes()).slice(-2);
|
||||||
|
const seconds = ('0' + t.getSeconds()).slice(-2);
|
||||||
|
|
||||||
this._internal.dateString = format
|
this._internal.dateString = format
|
||||||
.replace(/\{date\}/g, date)
|
.replace(/\{date\}/g, date)
|
||||||
.replace(/\{month\}/g, month)
|
.replace(/\{month\}/g, month)
|
||||||
.replace(/\{monthShort\}/g, monthShort)
|
.replace(/\{monthShort\}/g, monthShort)
|
||||||
.replace(/\{year\}/g, year)
|
.replace(/\{year\}/g, year)
|
||||||
.replace(/\{yearShort\}/g, yearShort)
|
.replace(/\{yearShort\}/g, yearShort)
|
||||||
.replace(/\{hours\}/g, hours)
|
.replace(/\{hours\}/g, hours)
|
||||||
.replace(/\{minutes\}/g, minutes)
|
.replace(/\{minutes\}/g, minutes)
|
||||||
.replace(/\{seconds\}/g, seconds);
|
.replace(/\{seconds\}/g, seconds);
|
||||||
|
} catch (error) {
|
||||||
|
// Set the output to be blank, makes it easier to handle.
|
||||||
|
this._internal.dateString = '';
|
||||||
|
this.flagOutputDirty('onError');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag that the value have changed
|
||||||
|
this.flagOutputDirty('currentValue');
|
||||||
|
this.sendSignalOnOutput('inputChanged');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ const StringFormatDefinition = {
|
|||||||
name: 'String Format',
|
name: 'String Format',
|
||||||
docs: 'https://docs.noodl.net/nodes/string-manipulation/string-format',
|
docs: 'https://docs.noodl.net/nodes/string-manipulation/string-format',
|
||||||
category: 'String Manipulation',
|
category: 'String Manipulation',
|
||||||
|
usePortAsLabel: 'format',
|
||||||
|
portLabelTruncationMode: 'length',
|
||||||
initialize() {
|
initialize() {
|
||||||
const internal = this._internal;
|
const internal = this._internal;
|
||||||
internal.format = '';
|
internal.format = '';
|
||||||
|
|||||||
@@ -227,6 +227,9 @@ declare namespace Noodl {
|
|||||||
objectOrId: string | { getId(): string; },
|
objectOrId: string | { getId(): string; },
|
||||||
options?: {
|
options?: {
|
||||||
className?: RecordClassName;
|
className?: RecordClassName;
|
||||||
|
keys?: string[] | string;
|
||||||
|
include?: string[] | string;
|
||||||
|
excludeKeys?: string[] | string;
|
||||||
}
|
}
|
||||||
): Promise<any>;
|
): Promise<any>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
const { merge } = require("webpack-merge");
|
const { merge } = require('webpack-merge');
|
||||||
const common = require("./webpack.viewer.common.js");
|
const common = require('./webpack.viewer.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: "development",
|
mode: 'development',
|
||||||
devtool: "inline-source-map",
|
devtool: 'inline-source-map',
|
||||||
watch: true,
|
watch: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ const { merge } = require('webpack-merge');
|
|||||||
const common = require('./webpack.viewer.common.js');
|
const common = require('./webpack.viewer.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: 'production'
|
mode: 'production',
|
||||||
|
devtool: 'source-map'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,12 +2,18 @@ const { RouterHandler } = require('../nodes/navigation/router-handler');
|
|||||||
const NoodlRuntime = require('@noodl/runtime');
|
const NoodlRuntime = require('@noodl/runtime');
|
||||||
|
|
||||||
const navigation = {
|
const navigation = {
|
||||||
|
/**
|
||||||
|
* This is set by "packages/noodl-viewer-react/src/noodl-js-api.js"
|
||||||
|
* @type {NoodlRuntime}
|
||||||
|
*/
|
||||||
|
_noodlRuntime: undefined,
|
||||||
|
|
||||||
async showPopup(componentPath, params) {
|
async showPopup(componentPath, params) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
navigation._noodlRuntime.context.showPopup(componentPath, params, {
|
navigation._noodlRuntime.context.showPopup(componentPath, params, {
|
||||||
onClosePopup: (action, results) => {
|
onClosePopup: (action, results) => {
|
||||||
resolve({
|
resolve({
|
||||||
action: action.replace('closeAction-', ''),
|
action: action?.replace('closeAction-', ''),
|
||||||
parameters: results
|
parameters: results
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export interface ButtonProps extends Noodl.ReactProps {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
buttonType: 'button' | 'submit';
|
buttonType: 'button' | 'submit';
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
textStyle: Noodl.TextStyle;
|
textStyle: Noodl.TextStyle;
|
||||||
|
|
||||||
useLabel: boolean;
|
useLabel: boolean;
|
||||||
@@ -96,6 +98,7 @@ export function Button(props: ButtonProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
{...props.attrs}
|
||||||
className={className}
|
className={className}
|
||||||
disabled={!props.enabled}
|
disabled={!props.enabled}
|
||||||
{...Utils.controlEvents(props)}
|
{...Utils.controlEvents(props)}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export interface CheckboxProps extends Noodl.ReactProps {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
useLabel: boolean;
|
useLabel: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
labelSpacing: string;
|
labelSpacing: string;
|
||||||
@@ -47,6 +49,7 @@ export function Checkbox(props: CheckboxProps) {
|
|||||||
Layout.align(style, props);
|
Layout.align(style, props);
|
||||||
|
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
className: [props.className, 'ndl-controls-checkbox-2'].join(' '),
|
className: [props.className, 'ndl-controls-checkbox-2'].join(' '),
|
||||||
disabled: !props.enabled,
|
disabled: !props.enabled,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export interface RadioButtonProps extends Noodl.ReactProps {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
value: string;
|
value: string;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
useLabel: boolean;
|
useLabel: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
labelSpacing: string;
|
labelSpacing: string;
|
||||||
@@ -47,6 +49,7 @@ export function RadioButton(props: RadioButtonProps) {
|
|||||||
props.checkedChanged && props.checkedChanged(radioButtonGroup ? radioButtonGroup.selected === props.value : false);
|
props.checkedChanged && props.checkedChanged(radioButtonGroup ? radioButtonGroup.selected === props.value : false);
|
||||||
|
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
disabled: !props.enabled,
|
disabled: !props.enabled,
|
||||||
className: [props.className, 'ndl-controls-radio-2'].join(' '),
|
className: [props.className, 'ndl-controls-radio-2'].join(' '),
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export interface SelectProps extends Noodl.ReactProps {
|
|||||||
textStyle: Noodl.TextStyle;
|
textStyle: Noodl.TextStyle;
|
||||||
items: TSFixme;
|
items: TSFixme;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
placeholderOpacity: string;
|
placeholderOpacity: string;
|
||||||
|
|
||||||
@@ -81,6 +83,7 @@ export function Select(props: SelectProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
className: props.className,
|
className: props.className,
|
||||||
style: {
|
style: {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export interface SliderProps extends Noodl.ReactProps {
|
|||||||
id: string;
|
id: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
value: number;
|
value: number;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
@@ -103,6 +105,7 @@ export function Slider(props: SliderProps) {
|
|||||||
const className = `ndl-controls-range2 ${instanceClassId} ${props.className ? props.className : ''} `;
|
const className = `ndl-controls-range2 ${instanceClassId} ${props.className ? props.className : ''} `;
|
||||||
|
|
||||||
const inputProps: React.InputHTMLAttributes<HTMLInputElement> = {
|
const inputProps: React.InputHTMLAttributes<HTMLInputElement> = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
style: {
|
style: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export interface TextInputProps extends Noodl.ReactProps {
|
|||||||
type: 'text' | 'textArea' | 'email' | 'number' | 'password' | 'url';
|
type: 'text' | 'textArea' | 'email' | 'number' | 'password' | 'url';
|
||||||
textStyle: Noodl.TextStyle;
|
textStyle: Noodl.TextStyle;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
@@ -149,6 +151,7 @@ export class TextInput extends React.Component<TextInputProps, State> {
|
|||||||
inputStyles.color = props.noodlNode.context.styles.resolveColor(inputStyles.color);
|
inputStyles.color = props.noodlNode.context.styles.resolveColor(inputStyles.color);
|
||||||
|
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
value: this.state.value,
|
value: this.state.value,
|
||||||
...Utils.controlEvents(props),
|
...Utils.controlEvents(props),
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export interface ColumnsProps extends Noodl.ReactProps {
|
|||||||
marginX: string;
|
marginX: string;
|
||||||
marginY: string;
|
marginY: string;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
justifyContent: 'flex-start' | 'flex-end' | 'center';
|
justifyContent: 'flex-start' | 'flex-end' | 'center';
|
||||||
direction: 'row' | 'column';
|
direction: 'row' | 'column';
|
||||||
minWidth: string;
|
minWidth: string;
|
||||||
@@ -115,7 +117,10 @@ export function Columns(props: ColumnsProps) {
|
|||||||
|
|
||||||
// ForEachCompoent breaks the layout but is needed to send onMount/onUnmount
|
// ForEachCompoent breaks the layout but is needed to send onMount/onUnmount
|
||||||
if (!Array.isArray(props.children)) {
|
if (!Array.isArray(props.children)) {
|
||||||
children = [props.children];
|
// @ts-expect-error props.children.type is any
|
||||||
|
if (props.children.type !== ForEachComponent) {
|
||||||
|
children = [props.children]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
children = props.children.filter((child) => child.type !== ForEachComponent);
|
children = props.children.filter((child) => child.type !== ForEachComponent);
|
||||||
forEachComponent = props.children.find((child) => child.type === ForEachComponent);
|
forEachComponent = props.children.find((child) => child.type === ForEachComponent);
|
||||||
@@ -123,9 +128,11 @@ export function Columns(props: ColumnsProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
{...props.attrs}
|
||||||
className={['columns-container', props.className].join(' ')}
|
className={['columns-container', props.className].join(' ')}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
style={{
|
style={{
|
||||||
|
visibility: containerWidth === null ? "hidden" : "visible",
|
||||||
marginTop: parseFloat(props.marginY) * -1,
|
marginTop: parseFloat(props.marginY) * -1,
|
||||||
marginLeft: parseFloat(props.marginX) * -1,
|
marginLeft: parseFloat(props.marginX) * -1,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ BScroll.use(Slide);
|
|||||||
export interface GroupProps extends Noodl.ReactProps {
|
export interface GroupProps extends Noodl.ReactProps {
|
||||||
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
|
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
scrollSnapEnabled: boolean;
|
scrollSnapEnabled: boolean;
|
||||||
showScrollbar: boolean;
|
showScrollbar: boolean;
|
||||||
scrollEnabled: boolean;
|
scrollEnabled: boolean;
|
||||||
@@ -271,6 +273,7 @@ export class Group extends React.Component<GroupProps> {
|
|||||||
<Component
|
<Component
|
||||||
// @ts-expect-error Lets hope that the type passed here is always static!
|
// @ts-expect-error Lets hope that the type passed here is always static!
|
||||||
className={props.className}
|
className={props.className}
|
||||||
|
{...props.attrs}
|
||||||
{...props.dom}
|
{...props.dom}
|
||||||
{...PointerListeners(props)}
|
{...PointerListeners(props)}
|
||||||
style={style}
|
style={style}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface ImageProps extends Noodl.ReactProps {
|
|||||||
src: string;
|
src: string;
|
||||||
onLoad?: () => void;
|
onLoad?: () => void;
|
||||||
};
|
};
|
||||||
|
attrs: React.Attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Image(props: ImageProps) {
|
export function Image(props: ImageProps) {
|
||||||
@@ -30,5 +31,5 @@ export function Image(props: ImageProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <img className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />;
|
return <img {...props.attrs} className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { Noodl } from '../../../types';
|
|||||||
export interface TextProps extends Noodl.ReactProps {
|
export interface TextProps extends Noodl.ReactProps {
|
||||||
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
|
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
textStyle: Noodl.TextStyle;
|
textStyle: Noodl.TextStyle;
|
||||||
text: string;
|
text: string;
|
||||||
|
|
||||||
@@ -48,6 +50,7 @@ export function Text(props: TextProps) {
|
|||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
className={['ndl-visual-text', props.className].join(' ')}
|
className={['ndl-visual-text', props.className].join(' ')}
|
||||||
|
{...props.attrs}
|
||||||
{...props.dom}
|
{...props.dom}
|
||||||
{...PointerListeners(props)}
|
{...PointerListeners(props)}
|
||||||
style={style}
|
style={style}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Node } = require('@noodl/runtime');
|
const { Node } = require('@noodl/runtime');
|
||||||
|
const Model = require('@noodl/runtime/src/model');
|
||||||
|
|
||||||
var Model = require('@noodl/runtime/src/model');
|
const VariableNodeDefinition = {
|
||||||
|
|
||||||
var VariableNodeDefinition = {
|
|
||||||
name: 'Variable',
|
name: 'Variable',
|
||||||
docs: 'https://docs.noodl.net/nodes/data/variable',
|
docs: 'https://docs.noodl.net/nodes/data/variable',
|
||||||
category: 'Data',
|
category: 'Data',
|
||||||
|
|||||||
@@ -30,11 +30,24 @@ const ButtonNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.layout = 'row'; //Used to tell child nodes what layout to expect
|
this.props.layout = 'row'; //Used to tell child nodes what layout to expect
|
||||||
},
|
},
|
||||||
getReactComponent() {
|
getReactComponent() {
|
||||||
return Button;
|
return Button;
|
||||||
},
|
},
|
||||||
|
inputs: {
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
inputCss: {
|
inputCss: {
|
||||||
backgroundColor: {
|
backgroundColor: {
|
||||||
index: 100,
|
index: 100,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const CheckBoxNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.sizeMode = 'explicit';
|
this.props.sizeMode = 'explicit';
|
||||||
this.props.id = 'input-' + guid();
|
this.props.id = 'input-' + guid();
|
||||||
this.props.checked = this._internal.checked = false;
|
this.props.checked = this._internal.checked = false;
|
||||||
@@ -94,6 +95,16 @@ const CheckBoxNode = {
|
|||||||
this.flagOutputDirty('checked');
|
this.flagOutputDirty('checked');
|
||||||
this._updateVisualState();
|
this._updateVisualState();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputCss: {
|
inputCss: {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const OptionsNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
|
this.props.attrs = {};
|
||||||
this._itemsChanged = () => {
|
this._itemsChanged = () => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
@@ -90,6 +91,16 @@ const OptionsNode = {
|
|||||||
this.flagOutputDirty('value');
|
this.flagOutputDirty('value');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputProps: {
|
inputProps: {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const RadioButtonNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.sizeMode = 'explicit';
|
this.props.sizeMode = 'explicit';
|
||||||
this.props.id = 'input-' + guid();
|
this.props.id = 'input-' + guid();
|
||||||
|
|
||||||
@@ -61,6 +62,16 @@ const RadioButtonNode = {
|
|||||||
set(value) {
|
set(value) {
|
||||||
this.setStyle({ backgroundColor: value }, 'fill');
|
this.setStyle({ backgroundColor: value }, 'fill');
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputProps: {
|
inputProps: {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const RangeNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.sizeMode = 'contentHeight';
|
this.props.sizeMode = 'contentHeight';
|
||||||
this.props.id = 'input-' + guid();
|
this.props.id = 'input-' + guid();
|
||||||
|
|
||||||
@@ -67,6 +68,16 @@ const RangeNode = {
|
|||||||
set(value) {
|
set(value) {
|
||||||
this._setInputValue(value);
|
this._setInputValue(value);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const TextInputNode = {
|
|||||||
return TextInput;
|
return TextInput;
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.startValue = '';
|
this.props.startValue = '';
|
||||||
this.props.id = this._internal.controlId = 'input-' + guid();
|
this.props.id = this._internal.controlId = 'input-' + guid();
|
||||||
},
|
},
|
||||||
@@ -167,6 +168,16 @@ const TextInputNode = {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputCss: {
|
inputCss: {
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ const ClosePopupNode = {
|
|||||||
this._internal.closeCallback = cb;
|
this._internal.closeCallback = cb;
|
||||||
},
|
},
|
||||||
scheduleClose: function () {
|
scheduleClose: function () {
|
||||||
var _this = this;
|
const _this = this;
|
||||||
var internal = this._internal;
|
const internal = this._internal;
|
||||||
if (!internal.hasScheduledClose) {
|
if (!internal.hasScheduledClose) {
|
||||||
internal.hasScheduledClose = true;
|
internal.hasScheduledClose = true;
|
||||||
this.scheduleAfterInputsHaveUpdated(function () {
|
this.scheduleAfterInputsHaveUpdated(function () {
|
||||||
@@ -51,8 +51,9 @@ const ClosePopupNode = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
close: function () {
|
close: function () {
|
||||||
if (this._internal.closeCallback)
|
if (this._internal.closeCallback) {
|
||||||
this._internal.closeCallback(this._internal.closeAction, this._internal.resultValues);
|
this._internal.closeCallback(this._internal.closeAction, this._internal.resultValues);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
closeActionTriggered: function (name) {
|
closeActionTriggered: function (name) {
|
||||||
this._internal.closeAction = name;
|
this._internal.closeAction = name;
|
||||||
@@ -112,9 +113,8 @@ module.exports = {
|
|||||||
var closeActions = node.parameters['closeActions'];
|
var closeActions = node.parameters['closeActions'];
|
||||||
if (closeActions) {
|
if (closeActions) {
|
||||||
closeActions = closeActions ? closeActions.split(',') : undefined;
|
closeActions = closeActions ? closeActions.split(',') : undefined;
|
||||||
for (var i in closeActions) {
|
for (const i in closeActions) {
|
||||||
var p = closeActions[i];
|
const p = closeActions[i];
|
||||||
|
|
||||||
ports.push({
|
ports.push({
|
||||||
type: 'signal',
|
type: 'signal',
|
||||||
plug: 'input',
|
plug: 'input',
|
||||||
|
|||||||
@@ -72,8 +72,10 @@ const Navigate = {
|
|||||||
backCallback: (action, results) => {
|
backCallback: (action, results) => {
|
||||||
this._internal.backResults = results;
|
this._internal.backResults = results;
|
||||||
|
|
||||||
for (var key in results) {
|
for (const key in results) {
|
||||||
if (this.hasOutput('backResult-' + key)) this.flagOutputDirty('backResult-' + key);
|
if (this.hasOutput('backResult-' + key)) {
|
||||||
|
this.flagOutputDirty('backResult-' + key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action !== undefined) this.sendSignalOnOutput(action);
|
if (action !== undefined) this.sendSignalOnOutput(action);
|
||||||
@@ -114,22 +116,23 @@ const Navigate = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'target')
|
if (name === 'target') {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setTargetPageId.bind(this)
|
set: this.setTargetPageId.bind(this)
|
||||||
});
|
});
|
||||||
else if (name === 'transition')
|
} else if (name === 'transition') {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setTransition.bind(this)
|
set: this.setTransition.bind(this)
|
||||||
});
|
});
|
||||||
else if (name.startsWith('tr-'))
|
} else if (name.startsWith('tr-')) {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setTransitionParam.bind(this, name.substring('tr-'.length))
|
set: this.setTransitionParam.bind(this, name.substring('tr-'.length))
|
||||||
});
|
});
|
||||||
else if (name.startsWith('pm-'))
|
} else if (name.startsWith('pm-')) {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setPageParam.bind(this, name.substring('pm-'.length))
|
set: this.setPageParam.bind(this, name.substring('pm-'.length))
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
registerOutputIfNeeded: function (name) {
|
registerOutputIfNeeded: function (name) {
|
||||||
if (this.hasOutput(name)) {
|
if (this.hasOutput(name)) {
|
||||||
@@ -174,18 +177,23 @@ function setup(context, graphModel) {
|
|||||||
if (Transitions[transition]) ports = ports.concat(Transitions[transition].ports(node.parameters));
|
if (Transitions[transition]) ports = ports.concat(Transitions[transition].ports(node.parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(node.parameters['stack'] !== undefined) {
|
const pageStacks = graphModel.getNodesWithType('Page Stack');
|
||||||
var pageStacks = graphModel.getNodesWithType('Page Stack');
|
const pageStack = pageStacks.find(
|
||||||
var pageStack = pageStacks.find(
|
|
||||||
(ps) => (ps.parameters['name'] || 'Main') === (node.parameters['stack'] || 'Main')
|
(ps) => (ps.parameters['name'] || 'Main') === (node.parameters['stack'] || 'Main')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pageStack !== undefined) {
|
if (pageStack !== undefined) {
|
||||||
var pages = pageStack.parameters['pages'];
|
const pages = pageStack.parameters['pages'];
|
||||||
if (pages !== undefined && pages.length > 0) {
|
if (pages !== undefined && pages.length > 0) {
|
||||||
ports.push({
|
ports.push({
|
||||||
plug: 'input',
|
plug: 'input',
|
||||||
type: { name: 'enum', enums: pages.map((p) => ({ label: p.label, value: p.id })), allowEditOnly: true },
|
type: {
|
||||||
|
name: 'enum',
|
||||||
|
enums: pages.map((p) => ({
|
||||||
|
label: p.label,
|
||||||
|
value: p.id
|
||||||
|
}))
|
||||||
|
},
|
||||||
group: 'General',
|
group: 'General',
|
||||||
displayName: 'Target Page',
|
displayName: 'Target Page',
|
||||||
name: 'target',
|
name: 'target',
|
||||||
@@ -193,14 +201,14 @@ function setup(context, graphModel) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// See if there is a target page with component
|
// See if there is a target page with component
|
||||||
var targetPageId = node.parameters['target'] || pages[0].id;
|
const targetPageId = node.parameters['target'] || pages[0].id;
|
||||||
var targetComponentName = pageStack.parameters['pageComp-' + targetPageId];
|
const targetComponentName = pageStack.parameters['pageComp-' + targetPageId];
|
||||||
if (targetComponentName !== undefined) {
|
if (targetComponentName !== undefined) {
|
||||||
const component = graphModel.components[targetComponentName];
|
const component = graphModel.components[targetComponentName];
|
||||||
|
|
||||||
if (component !== undefined) {
|
if (component !== undefined) {
|
||||||
// Make all inputs of the component to inputs of this navigation node
|
// Make all inputs of the component to inputs of this navigation node
|
||||||
for (var inputName in component.inputPorts) {
|
for (const inputName in component.inputPorts) {
|
||||||
ports.push({
|
ports.push({
|
||||||
name: 'pm-' + inputName,
|
name: 'pm-' + inputName,
|
||||||
displayName: inputName,
|
displayName: inputName,
|
||||||
@@ -245,7 +253,6 @@ function setup(context, graphModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
context.editorConnection.sendDynamicPorts(node.id, ports);
|
context.editorConnection.sendDynamicPorts(node.id, ports);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
import ASyncQueue from '../../async-queue';
|
import ASyncQueue from '../../async-queue';
|
||||||
import { createNodeFromReactComponent } from '../../react-component-node';
|
import { createNodeFromReactComponent } from '../../react-component-node';
|
||||||
|
|
||||||
@@ -75,10 +77,13 @@ const PageStack = {
|
|||||||
const info = [{ type: 'text', value: 'Active Components:' }];
|
const info = [{ type: 'text', value: 'Active Components:' }];
|
||||||
|
|
||||||
return info.concat(
|
return info.concat(
|
||||||
this._internal.stack.map((p, i) => ({
|
this._internal.stack.map((p) => {
|
||||||
type: 'text',
|
const pageInfo = this._findPage(p.pageId);
|
||||||
value: '- ' + this._internal.pages.find((pi) => pi.id === p.pageId).label
|
return {
|
||||||
}))
|
type: 'text',
|
||||||
|
value: '- ' + pageInfo.label
|
||||||
|
};
|
||||||
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
defaultCss: {
|
defaultCss: {
|
||||||
@@ -170,6 +175,7 @@ const PageStack = {
|
|||||||
topPageName: {
|
topPageName: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
displayName: 'Top Component Name',
|
displayName: 'Top Component Name',
|
||||||
|
group: 'General',
|
||||||
get() {
|
get() {
|
||||||
return this._internal.topPageName;
|
return this._internal.topPageName;
|
||||||
}
|
}
|
||||||
@@ -177,6 +183,7 @@ const PageStack = {
|
|||||||
stackDepth: {
|
stackDepth: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
displayName: 'Stack Depth',
|
displayName: 'Stack Depth',
|
||||||
|
group: 'General',
|
||||||
get() {
|
get() {
|
||||||
return this._internal.stackDepth;
|
return this._internal.stackDepth;
|
||||||
}
|
}
|
||||||
@@ -189,12 +196,31 @@ const PageStack = {
|
|||||||
_deregisterPageStack() {
|
_deregisterPageStack() {
|
||||||
NavigationHandler.instance.deregisterPageStack(this._internal.name, this);
|
NavigationHandler.instance.deregisterPageStack(this._internal.name, this);
|
||||||
},
|
},
|
||||||
_pageNameForId(id) {
|
/**
|
||||||
if (this._internal.pages === undefined) return;
|
* @param {String} pageIdOrLabel
|
||||||
const page = this._internal.pages.find((p) => p.id === id);
|
*/
|
||||||
if (page === undefined) return;
|
_findPage(pageIdOrLabel) {
|
||||||
|
if (this._internal.pageInfo[pageIdOrLabel]) {
|
||||||
|
const pageInfo = this._internal.pageInfo[pageIdOrLabel];
|
||||||
|
const pageRef = this._internal.pages.find((x) => x.id === pageIdOrLabel);
|
||||||
|
return {
|
||||||
|
component: String(pageInfo.component),
|
||||||
|
label: String(pageRef.label),
|
||||||
|
id: String(pageIdOrLabel)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return page.label;
|
const pageRef = this._internal.pages.find((x) => x.label === pageIdOrLabel);
|
||||||
|
if (pageRef) {
|
||||||
|
const pageInfo = this._internal.pageInfo[pageRef.id];
|
||||||
|
return {
|
||||||
|
component: String(pageInfo.component),
|
||||||
|
label: String(pageRef.label),
|
||||||
|
id: String(pageRef.id)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
setPageOutputs(outputs) {
|
setPageOutputs(outputs) {
|
||||||
for (const prop in outputs) {
|
for (const prop in outputs) {
|
||||||
@@ -230,8 +256,9 @@ const PageStack = {
|
|||||||
|
|
||||||
if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
|
if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
|
||||||
|
|
||||||
var startPageId,
|
let startPageId;
|
||||||
params = {};
|
let params = {};
|
||||||
|
|
||||||
var pageFromUrl = this.matchPageFromUrl();
|
var pageFromUrl = this.matchPageFromUrl();
|
||||||
if (pageFromUrl !== undefined) {
|
if (pageFromUrl !== undefined) {
|
||||||
// We have an url matching a page, use that page as start page
|
// We have an url matching a page, use that page as start page
|
||||||
@@ -239,13 +266,16 @@ const PageStack = {
|
|||||||
|
|
||||||
params = Object.assign({}, pageFromUrl.query, pageFromUrl.params);
|
params = Object.assign({}, pageFromUrl.query, pageFromUrl.params);
|
||||||
} else {
|
} else {
|
||||||
var startPageId = this._internal.startPageId;
|
startPageId = this._internal.startPageId;
|
||||||
if (startPageId === undefined) startPageId = this._internal.pages[0].id;
|
if (startPageId === undefined) startPageId = this._internal.pages[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pageInfo = this._internal.pageInfo[startPageId];
|
// Find the page by either ID or by Label
|
||||||
|
const pageInfo = this._findPage(startPageId);
|
||||||
if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
|
if (pageInfo === undefined || pageInfo.component === undefined) {
|
||||||
|
// No page was found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var content = await this.nodeScope.createNode(pageInfo.component, guid());
|
var content = await this.nodeScope.createNode(pageInfo.component, guid());
|
||||||
|
|
||||||
@@ -269,7 +299,7 @@ const PageStack = {
|
|||||||
];
|
];
|
||||||
|
|
||||||
this.setPageOutputs({
|
this.setPageOutputs({
|
||||||
topPageName: this._pageNameForId(startPageId),
|
topPageName: pageInfo.label,
|
||||||
stackDepth: this._internal.stack.length
|
stackDepth: this._internal.stack.length
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -458,13 +488,22 @@ const PageStack = {
|
|||||||
this._internal.asyncQueue.enqueue(this.replaceAsync.bind(this, args));
|
this._internal.asyncQueue.enqueue(this.replaceAsync.bind(this, args));
|
||||||
},
|
},
|
||||||
async replaceAsync(args) {
|
async replaceAsync(args) {
|
||||||
if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
|
if (this._internal.pages === undefined || this._internal.pages.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._internal.isTransitioning) return;
|
if (this._internal.isTransitioning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var pageId = args.target || this._internal.pages[0].id;
|
const pageId = args.target || this._internal.pages[0].id;
|
||||||
var pageInfo = this._internal.pageInfo[pageId];
|
|
||||||
if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
|
// Find the page by either ID or by Label
|
||||||
|
const pageInfo = this._findPage(pageId);
|
||||||
|
if (pageInfo === undefined || pageInfo.component === undefined) {
|
||||||
|
// No page was found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove all current pages in the stack
|
// Remove all current pages in the stack
|
||||||
var children = this.getChildren();
|
var children = this.getChildren();
|
||||||
@@ -498,7 +537,7 @@ const PageStack = {
|
|||||||
];
|
];
|
||||||
|
|
||||||
this.setPageOutputs({
|
this.setPageOutputs({
|
||||||
topPageName: this._pageNameForId(pageId),
|
topPageName: pageInfo.label,
|
||||||
stackDepth: this._internal.stack.length
|
stackDepth: this._internal.stack.length
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -510,13 +549,22 @@ const PageStack = {
|
|||||||
this._internal.asyncQueue.enqueue(this.navigateAsync.bind(this, args));
|
this._internal.asyncQueue.enqueue(this.navigateAsync.bind(this, args));
|
||||||
},
|
},
|
||||||
async navigateAsync(args) {
|
async navigateAsync(args) {
|
||||||
if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
|
if (this._internal.pages === undefined || this._internal.pages.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._internal.isTransitioning) return;
|
if (this._internal.isTransitioning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var pageId = args.target || this._internal.pages[0].id;
|
const pageId = args.target || this._internal.pages[0].id;
|
||||||
var pageInfo = this._internal.pageInfo[pageId];
|
|
||||||
if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
|
// Find the page by either ID or by Label
|
||||||
|
const pageInfo = this._findPage(pageId);
|
||||||
|
if (pageInfo === undefined || pageInfo.component === undefined) {
|
||||||
|
// No page was found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create the container group
|
// Create the container group
|
||||||
const group = this.createPageContainer();
|
const group = this.createPageContainer();
|
||||||
@@ -530,7 +578,7 @@ const PageStack = {
|
|||||||
group.addChild(content);
|
group.addChild(content);
|
||||||
|
|
||||||
// Connect navigate back nodes
|
// Connect navigate back nodes
|
||||||
var navigateBackNodes = content.nodeScope.getNodesWithType('PageStackNavigateBack');
|
const navigateBackNodes = content.nodeScope.getNodesWithType('PageStackNavigateBack');
|
||||||
if (navigateBackNodes && navigateBackNodes.length > 0) {
|
if (navigateBackNodes && navigateBackNodes.length > 0) {
|
||||||
for (var j = 0; j < navigateBackNodes.length; j++) {
|
for (var j = 0; j < navigateBackNodes.length; j++) {
|
||||||
navigateBackNodes[j]._setBackCallback(this.back.bind(this));
|
navigateBackNodes[j]._setBackCallback(this.back.bind(this));
|
||||||
@@ -538,8 +586,8 @@ const PageStack = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push the new top
|
// Push the new top
|
||||||
var top = this._internal.stack[this._internal.stack.length - 1];
|
const top = this._internal.stack[this._internal.stack.length - 1];
|
||||||
var newTop = {
|
const newTop = {
|
||||||
from: top.page,
|
from: top.page,
|
||||||
page: group,
|
page: group,
|
||||||
pageInfo: pageInfo,
|
pageInfo: pageInfo,
|
||||||
@@ -551,7 +599,7 @@ const PageStack = {
|
|||||||
};
|
};
|
||||||
this._internal.stack.push(newTop);
|
this._internal.stack.push(newTop);
|
||||||
this.setPageOutputs({
|
this.setPageOutputs({
|
||||||
topPageName: this._pageNameForId(args.target),
|
topPageName: pageInfo.label,
|
||||||
stackDepth: this._internal.stack.length
|
stackDepth: this._internal.stack.length
|
||||||
});
|
});
|
||||||
this._updateUrlWithTopPage();
|
this._updateUrlWithTopPage();
|
||||||
@@ -584,8 +632,11 @@ const PageStack = {
|
|||||||
this.addChild(top.from, 0);
|
this.addChild(top.from, 0);
|
||||||
top.backCallback && top.backCallback(args.backAction, args.results);
|
top.backCallback && top.backCallback(args.backAction, args.results);
|
||||||
|
|
||||||
|
// Find the page by either ID or by Label
|
||||||
|
const pageInfo = this._findPage(this._internal.stack[this._internal.stack.length - 2].pageId);
|
||||||
|
|
||||||
this.setPageOutputs({
|
this.setPageOutputs({
|
||||||
topPageName: this._pageNameForId(this._internal.stack[this._internal.stack.length - 2].pageId),
|
topPageName: pageInfo.label,
|
||||||
stackDepth: this._internal.stack.length - 1
|
stackDepth: this._internal.stack.length - 1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -53,15 +53,24 @@ const ShowPopupNode = {
|
|||||||
|
|
||||||
this.context.showPopup(this._internal.target, this._internal.popupParams, {
|
this.context.showPopup(this._internal.target, this._internal.popupParams, {
|
||||||
senderNode: this.nodeScope.componentOwner,
|
senderNode: this.nodeScope.componentOwner,
|
||||||
|
/**
|
||||||
|
* @param {string | undefined} action
|
||||||
|
* @param {*} results
|
||||||
|
*/
|
||||||
onClosePopup: (action, results) => {
|
onClosePopup: (action, results) => {
|
||||||
this._internal.closeResults = results;
|
this._internal.closeResults = results;
|
||||||
|
|
||||||
for (var key in results) {
|
for (const key in results) {
|
||||||
if (this.hasOutput('closeResult-' + key)) this.flagOutputDirty('closeResult-' + key);
|
if (this.hasOutput('closeResult-' + key)) {
|
||||||
|
this.flagOutputDirty('closeResult-' + key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!action) this.sendSignalOnOutput('Closed');
|
if (!action) {
|
||||||
else this.sendSignalOnOutput(action);
|
this.sendSignalOnOutput('Closed');
|
||||||
|
} else {
|
||||||
|
this.sendSignalOnOutput(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,17 @@ const CloudStore = require('@noodl/runtime/src/api/cloudstore');
|
|||||||
|
|
||||||
('use strict');
|
('use strict');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @param {{
|
||||||
|
* endpoint: string;
|
||||||
|
* method: string;
|
||||||
|
* appId: string;
|
||||||
|
* content: any;
|
||||||
|
* success: (json: object) => void;
|
||||||
|
* error: (json: object | undefined) => void;
|
||||||
|
* }} options
|
||||||
|
*/
|
||||||
function _makeRequest(path, options) {
|
function _makeRequest(path, options) {
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
@@ -15,7 +26,9 @@ function _makeRequest(path, options) {
|
|||||||
|
|
||||||
if (xhr.status === 200 || xhr.status === 201) {
|
if (xhr.status === 200 || xhr.status === 201) {
|
||||||
options.success(json);
|
options.success(json);
|
||||||
} else options.error(json);
|
} else {
|
||||||
|
options.error(json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -191,7 +204,7 @@ var CloudFunctionNode = {
|
|||||||
this.sendSignalOnOutput('success');
|
this.sendSignalOnOutput('success');
|
||||||
},
|
},
|
||||||
error: (e) => {
|
error: (e) => {
|
||||||
const error = typeof e === 'string' ? e : e.error || 'Failed running cloud function.';
|
const error = typeof e === 'string' ? e : e?.error || 'Failed running cloud function.';
|
||||||
this._internal.lastCallResult = {
|
this._internal.lastCallResult = {
|
||||||
status: 'failure',
|
status: 'failure',
|
||||||
error
|
error
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
const { Node } = require('@noodl/runtime');
|
const { Node } = require('@noodl/runtime');
|
||||||
|
|
||||||
var Model = require('@noodl/runtime/src/model'),
|
const Collection = require('@noodl/runtime/src/collection');
|
||||||
Collection = require('@noodl/runtime/src/collection');
|
|
||||||
|
|
||||||
var CollectionNode = {
|
var CollectionNode = {
|
||||||
name: 'Collection2',
|
name: 'Collection2',
|
||||||
@@ -27,6 +26,7 @@ var CollectionNode = {
|
|||||||
|
|
||||||
_this.scheduleAfterInputsHaveUpdated(function () {
|
_this.scheduleAfterInputsHaveUpdated(function () {
|
||||||
_this.sendSignalOnOutput('changed');
|
_this.sendSignalOnOutput('changed');
|
||||||
|
_this.flagOutputDirty('firstItemId');
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
collectionChangedScheduled = false;
|
collectionChangedScheduled = false;
|
||||||
});
|
});
|
||||||
@@ -117,6 +117,17 @@ var CollectionNode = {
|
|||||||
return this._internal.collection;
|
return this._internal.collection;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
firstItemId: {
|
||||||
|
type: 'string',
|
||||||
|
displayName: 'First Item Id',
|
||||||
|
group: 'General',
|
||||||
|
getter: function () {
|
||||||
|
if (this._internal.collection) {
|
||||||
|
var firstItem = this._internal.collection.get(0);
|
||||||
|
if (firstItem !== undefined) return firstItem.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
count: {
|
count: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
displayName: 'Count',
|
displayName: 'Count',
|
||||||
@@ -150,6 +161,7 @@ var CollectionNode = {
|
|||||||
collection.on('change', this._internal.collectionChangedCallback);
|
collection.on('change', this._internal.collectionChangedCallback);
|
||||||
|
|
||||||
this.flagOutputDirty('items');
|
this.flagOutputDirty('items');
|
||||||
|
this.flagOutputDirty('firstItemId');
|
||||||
this.flagOutputDirty('count');
|
this.flagOutputDirty('count');
|
||||||
},
|
},
|
||||||
setSourceCollection: function (collection) {
|
setSourceCollection: function (collection) {
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Node } = require('@noodl/runtime');
|
|
||||||
|
|
||||||
const Model = require('@noodl/runtime/src/model');
|
const Model = require('@noodl/runtime/src/model');
|
||||||
const Collection = require('@noodl/runtime/src/collection');
|
const Collection = require('@noodl/runtime/src/collection');
|
||||||
|
|
||||||
var SetVariableNodeDefinition = {
|
const SetVariableNodeDefinition = {
|
||||||
name: 'Set Variable',
|
name: 'Set Variable',
|
||||||
docs: 'https://docs.noodl.net/nodes/data/variable/set-variable',
|
docs: 'https://docs.noodl.net/nodes/data/variable/set-variable',
|
||||||
category: 'Data',
|
category: 'Data',
|
||||||
usePortAsLabel: 'name',
|
usePortAsLabel: 'name',
|
||||||
color: 'data',
|
color: 'data',
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
var internal = this._internal;
|
const internal = this._internal;
|
||||||
|
|
||||||
internal.variablesModel = Model.get('--ndl--global-variables');
|
internal.variablesModel = Model.get('--ndl--global-variables');
|
||||||
},
|
},
|
||||||
|
getInspectInfo() {
|
||||||
|
if (this._internal.name) {
|
||||||
|
return this._internal.variablesModel.get(this._internal.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '[No value set]';
|
||||||
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
done: {
|
done: {
|
||||||
type: 'signal',
|
type: 'signal',
|
||||||
@@ -74,17 +78,22 @@ var SetVariableNodeDefinition = {
|
|||||||
if (this.hasScheduledStore) return;
|
if (this.hasScheduledStore) return;
|
||||||
this.hasScheduledStore = true;
|
this.hasScheduledStore = true;
|
||||||
|
|
||||||
var internal = this._internal;
|
const internal = this._internal;
|
||||||
this.scheduleAfterInputsHaveUpdated(function () {
|
this.scheduleAfterInputsHaveUpdated(function () {
|
||||||
this.hasScheduledStore = false;
|
this.hasScheduledStore = false;
|
||||||
|
|
||||||
var value = internal.setWith === 'emptyString' ? '' : internal.value;
|
let value = internal.setWith === 'emptyString' ? '' : internal.value;
|
||||||
|
|
||||||
|
// Can set arrays with "id" or array
|
||||||
|
if (internal.setWith === 'object' && typeof value === 'string') value = Model.get(value);
|
||||||
|
|
||||||
|
// Can set arrays with "id" or array
|
||||||
|
if (internal.setWith === 'array' && typeof value === 'string') value = Collection.get(value);
|
||||||
|
|
||||||
if (internal.setWith === 'object' && typeof value === 'string') value = Model.get(value); // Can set arrays with "id" or array
|
|
||||||
if (internal.setWith === 'array' && typeof value === 'string') value = Collection.get(value); // Can set arrays with "id" or array
|
|
||||||
if (internal.setWith === 'boolean') value = !!value;
|
if (internal.setWith === 'boolean') value = !!value;
|
||||||
|
|
||||||
//use forceChange to always trigger Variable nodes to send the value on their output, even if it's the same value twice
|
// use forceChange to always trigger Variable nodes to send the value on
|
||||||
|
// their output, even if it's the same value twice
|
||||||
internal.variablesModel.set(internal.name, value, {
|
internal.variablesModel.set(internal.name, value, {
|
||||||
forceChange: true
|
forceChange: true
|
||||||
});
|
});
|
||||||
@@ -96,10 +105,11 @@ var SetVariableNodeDefinition = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'value')
|
if (name === 'value') {
|
||||||
this.registerInput(name, {
|
this.registerInput(name, {
|
||||||
set: this.setValue.bind(this)
|
set: this.setValue.bind(this)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ const OpenFilePicker = {
|
|||||||
|
|
||||||
input.accept = this._internal.acceptedFileTypes;
|
input.accept = this._internal.acceptedFileTypes;
|
||||||
|
|
||||||
|
if (this._internal.capture) {
|
||||||
|
input.capture = this._internal.capture;
|
||||||
|
}
|
||||||
|
|
||||||
input.onchange = onChange;
|
input.onchange = onChange;
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
@@ -54,6 +58,14 @@ const OpenFilePicker = {
|
|||||||
set(value) {
|
set(value) {
|
||||||
this._internal.acceptedFileTypes = value;
|
this._internal.acceptedFileTypes = value;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
capture: {
|
||||||
|
group: 'General',
|
||||||
|
type: 'string',
|
||||||
|
displayName: 'Capture',
|
||||||
|
set(value) {
|
||||||
|
this._internal.capture = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const Switch = {
|
|||||||
name: 'Switch',
|
name: 'Switch',
|
||||||
docs: 'https://docs.noodl.net/nodes/logic/switch',
|
docs: 'https://docs.noodl.net/nodes/logic/switch',
|
||||||
category: 'Logic',
|
category: 'Logic',
|
||||||
initialize: function () {
|
initialize() {
|
||||||
this._internal.state = false;
|
this._internal.state = false;
|
||||||
this._internal.initialized = false;
|
this._internal.initialized = false;
|
||||||
},
|
},
|
||||||
@@ -15,7 +15,7 @@ const Switch = {
|
|||||||
on: {
|
on: {
|
||||||
displayName: 'On',
|
displayName: 'On',
|
||||||
group: 'Change State',
|
group: 'Change State',
|
||||||
valueChangedToTrue: function () {
|
valueChangedToTrue() {
|
||||||
if (this._internal.state === true) {
|
if (this._internal.state === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ const Switch = {
|
|||||||
off: {
|
off: {
|
||||||
displayName: 'Off',
|
displayName: 'Off',
|
||||||
group: 'Change State',
|
group: 'Change State',
|
||||||
valueChangedToTrue: function () {
|
valueChangedToTrue() {
|
||||||
if (this._internal.state === false) {
|
if (this._internal.state === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ const Switch = {
|
|||||||
flip: {
|
flip: {
|
||||||
displayName: 'Flip',
|
displayName: 'Flip',
|
||||||
group: 'Change State',
|
group: 'Change State',
|
||||||
valueChangedToTrue: function () {
|
valueChangedToTrue() {
|
||||||
this._internal.state = !this._internal.state;
|
this._internal.state = !this._internal.state;
|
||||||
this.flagOutputDirty('state');
|
this.flagOutputDirty('state');
|
||||||
this.emitSignals();
|
this.emitSignals();
|
||||||
@@ -50,7 +50,7 @@ const Switch = {
|
|||||||
displayName: 'State',
|
displayName: 'State',
|
||||||
group: 'General',
|
group: 'General',
|
||||||
default: false,
|
default: false,
|
||||||
set: function (value) {
|
set(value) {
|
||||||
this._internal.state = !!value;
|
this._internal.state = !!value;
|
||||||
this.flagOutputDirty('state');
|
this.flagOutputDirty('state');
|
||||||
this.emitSignals();
|
this.emitSignals();
|
||||||
@@ -61,10 +61,15 @@ const Switch = {
|
|||||||
state: {
|
state: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
displayName: 'Current State',
|
displayName: 'Current State',
|
||||||
getter: function () {
|
getter() {
|
||||||
return this._internal.state;
|
return this._internal.state;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
switched: {
|
||||||
|
displayName: 'Switched',
|
||||||
|
type: 'signal',
|
||||||
|
group: 'Signals'
|
||||||
|
},
|
||||||
switchedToOn: {
|
switchedToOn: {
|
||||||
displayName: 'Switched To On',
|
displayName: 'Switched To On',
|
||||||
type: 'signal',
|
type: 'signal',
|
||||||
@@ -77,12 +82,13 @@ const Switch = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
prototypeExtensions: {
|
prototypeExtensions: {
|
||||||
emitSignals: function () {
|
emitSignals() {
|
||||||
if (this._internal.state === true) {
|
if (this._internal.state === true) {
|
||||||
this.sendSignalOnOutput('switchedToOn');
|
this.sendSignalOnOutput('switchedToOn');
|
||||||
} else {
|
} else {
|
||||||
this.sendSignalOnOutput('switchedToOff');
|
this.sendSignalOnOutput('switchedToOff');
|
||||||
}
|
}
|
||||||
|
this.sendSignalOnOutput('switched');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
|
|
||||||
const UserService = require('./userservice');
|
const UserService = require('./userservice');
|
||||||
|
|
||||||
var LoginNodeDefinition = {
|
const LoginNodeDefinition = {
|
||||||
name: 'net.noodl.user.LogIn',
|
name: 'net.noodl.user.LogIn',
|
||||||
docs: 'https://docs.noodl.net/nodes/data/user/log-in',
|
docs: 'https://docs.noodl.net/nodes/data/user/log-in',
|
||||||
displayNodeName: 'Log In',
|
displayNodeName: 'Log In',
|
||||||
category: 'Cloud Services',
|
category: 'Cloud Services',
|
||||||
color: 'data',
|
color: 'data',
|
||||||
initialize: function () {
|
|
||||||
var internal = this._internal;
|
|
||||||
},
|
|
||||||
getInspectInfo() {},
|
|
||||||
outputs: {
|
outputs: {
|
||||||
success: {
|
success: {
|
||||||
type: 'signal',
|
type: 'signal',
|
||||||
@@ -28,7 +23,7 @@ var LoginNodeDefinition = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
displayName: 'Error',
|
displayName: 'Error',
|
||||||
group: 'Error',
|
group: 'Error',
|
||||||
getter: function () {
|
getter() {
|
||||||
return this._internal.error;
|
return this._internal.error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +32,7 @@ var LoginNodeDefinition = {
|
|||||||
login: {
|
login: {
|
||||||
displayName: 'Do',
|
displayName: 'Do',
|
||||||
group: 'Actions',
|
group: 'Actions',
|
||||||
valueChangedToTrue: function () {
|
valueChangedToTrue() {
|
||||||
this.scheduleLogIn();
|
this.scheduleLogIn();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -45,7 +40,7 @@ var LoginNodeDefinition = {
|
|||||||
displayName: 'Username',
|
displayName: 'Username',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
group: 'General',
|
group: 'General',
|
||||||
set: function (value) {
|
set(value) {
|
||||||
this._internal.username = value;
|
this._internal.username = value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -53,13 +48,13 @@ var LoginNodeDefinition = {
|
|||||||
displayName: 'Password',
|
displayName: 'Password',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
group: 'General',
|
group: 'General',
|
||||||
set: function (value) {
|
set(value) {
|
||||||
this._internal.password = value;
|
this._internal.password = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setError: function (err) {
|
setError(err) {
|
||||||
this._internal.error = err;
|
this._internal.error = err;
|
||||||
this.flagOutputDirty('error');
|
this.flagOutputDirty('error');
|
||||||
this.sendSignalOnOutput('failure');
|
this.sendSignalOnOutput('failure');
|
||||||
@@ -76,9 +71,7 @@ var LoginNodeDefinition = {
|
|||||||
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
|
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scheduleLogIn: function () {
|
scheduleLogIn() {
|
||||||
const internal = this._internal;
|
|
||||||
|
|
||||||
if (this.logInScheduled === true) return;
|
if (this.logInScheduled === true) return;
|
||||||
this.logInScheduled = true;
|
this.logInScheduled = true;
|
||||||
|
|
||||||
@@ -100,89 +93,7 @@ var LoginNodeDefinition = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*function updatePorts(nodeId, parameters, editorConnection, dbCollections) {
|
|
||||||
var ports = [];
|
|
||||||
|
|
||||||
ports.push({
|
|
||||||
name: 'collectionName',
|
|
||||||
displayName: "Class",
|
|
||||||
group: "General",
|
|
||||||
type: { name: 'enum', enums: (dbCollections !== undefined) ? dbCollections.map((c) => { return { value: c.name, label: c.name } }) : [], allowEditOnly: true },
|
|
||||||
plug: 'input'
|
|
||||||
})
|
|
||||||
|
|
||||||
if (parameters.collectionName && dbCollections) {
|
|
||||||
// Fetch ports from collection keys
|
|
||||||
var c = dbCollections.find((c) => c.name === parameters.collectionName);
|
|
||||||
if (c && c.schema && c.schema.properties) {
|
|
||||||
var props = c.schema.properties;
|
|
||||||
for (var key in props) {
|
|
||||||
var p = props[key];
|
|
||||||
if (ports.find((_p) => _p.name === key)) continue;
|
|
||||||
|
|
||||||
if(p.type === 'Relation') {
|
|
||||||
|
|
||||||
}
|
|
||||||
else { // Other schema type ports
|
|
||||||
const _typeMap = {
|
|
||||||
"String":"string",
|
|
||||||
"Boolean":"boolean",
|
|
||||||
"Number":"number",
|
|
||||||
"Date":"date"
|
|
||||||
}
|
|
||||||
|
|
||||||
ports.push({
|
|
||||||
type: {
|
|
||||||
name: _typeMap[p.type]?_typeMap[p.type]:'*',
|
|
||||||
},
|
|
||||||
plug: 'output',
|
|
||||||
group: 'Properties',
|
|
||||||
name: 'prop-' + key,
|
|
||||||
displayName: key,
|
|
||||||
})
|
|
||||||
|
|
||||||
ports.push({
|
|
||||||
type: 'signal',
|
|
||||||
plug: 'output',
|
|
||||||
group: 'Changed Events',
|
|
||||||
displayName: key+ ' Changed',
|
|
||||||
name: 'changed-' + key,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
editorConnection.sendDynamicPorts(nodeId, ports);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
node: LoginNodeDefinition,
|
node: LoginNodeDefinition,
|
||||||
setup: function (context, graphModel) {
|
setup(_context, _graphModel) {}
|
||||||
/* if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _managePortsForNode(node) {
|
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('dbCollections'));
|
|
||||||
|
|
||||||
node.on("parameterUpdated", function (event) {
|
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('dbCollections'));
|
|
||||||
});
|
|
||||||
|
|
||||||
graphModel.on('metadataChanged.dbCollections', function (data) {
|
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, data);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
graphModel.on("editorImportComplete", ()=> {
|
|
||||||
graphModel.on("nodeAdded.DbModel2", function (node) {
|
|
||||||
_managePortsForNode(node)
|
|
||||||
})
|
|
||||||
|
|
||||||
for(const node of graphModel.getNodesWithType('DbModel2')) {
|
|
||||||
_managePortsForNode(node)
|
|
||||||
}
|
|
||||||
})*/
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
|
|
||||||
const UserService = require('./userservice');
|
const UserService = require('./userservice');
|
||||||
|
|
||||||
var LogOutNodeDefinition = {
|
const LogOutNodeDefinition = {
|
||||||
name: 'net.noodl.user.LogOut',
|
name: 'net.noodl.user.LogOut',
|
||||||
docs: 'https://docs.noodl.net/nodes/data/user/log-out',
|
docs: 'https://docs.noodl.net/nodes/data/user/log-out',
|
||||||
displayNodeName: 'Log Out',
|
displayNodeName: 'Log Out',
|
||||||
category: 'Cloud Services',
|
category: 'Cloud Services',
|
||||||
color: 'data',
|
color: 'data',
|
||||||
initialize: function () {
|
|
||||||
var internal = this._internal;
|
|
||||||
},
|
|
||||||
getInspectInfo() {},
|
|
||||||
outputs: {
|
outputs: {
|
||||||
success: {
|
success: {
|
||||||
type: 'signal',
|
type: 'signal',
|
||||||
@@ -28,7 +23,7 @@ var LogOutNodeDefinition = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
displayName: 'Error',
|
displayName: 'Error',
|
||||||
group: 'Error',
|
group: 'Error',
|
||||||
getter: function () {
|
getter() {
|
||||||
return this._internal.error;
|
return this._internal.error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,13 +32,13 @@ var LogOutNodeDefinition = {
|
|||||||
login: {
|
login: {
|
||||||
displayName: 'Do',
|
displayName: 'Do',
|
||||||
group: 'Actions',
|
group: 'Actions',
|
||||||
valueChangedToTrue: function () {
|
valueChangedToTrue() {
|
||||||
this.scheduleLogOut();
|
this.scheduleLogOut();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setError: function (err) {
|
setError(err) {
|
||||||
this._internal.error = err;
|
this._internal.error = err;
|
||||||
this.flagOutputDirty('error');
|
this.flagOutputDirty('error');
|
||||||
this.sendSignalOnOutput('failure');
|
this.sendSignalOnOutput('failure');
|
||||||
@@ -60,9 +55,7 @@ var LogOutNodeDefinition = {
|
|||||||
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
|
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scheduleLogOut: function () {
|
scheduleLogOut() {
|
||||||
const internal = this._internal;
|
|
||||||
|
|
||||||
if (this.logOutScheduled === true) return;
|
if (this.logOutScheduled === true) return;
|
||||||
this.logOutScheduled = true;
|
this.logOutScheduled = true;
|
||||||
|
|
||||||
@@ -84,5 +77,5 @@ var LogOutNodeDefinition = {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
node: LogOutNodeDefinition,
|
node: LogOutNodeDefinition,
|
||||||
setup: function (context, graphModel) {}
|
setup(_context, _graphModel) {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
|
|
||||||
const UserService = require('./userservice');
|
const UserService = require('./userservice');
|
||||||
|
|
||||||
var SignUpNodeDefinition = {
|
const SignUpNodeDefinition = {
|
||||||
name: 'net.noodl.user.SignUp',
|
name: 'net.noodl.user.SignUp',
|
||||||
docs: 'https://docs.noodl.net/nodes/data/user/sign-up',
|
docs: 'https://docs.noodl.net/nodes/data/user/sign-up',
|
||||||
displayNodeName: 'Sign Up',
|
displayNodeName: 'Sign Up',
|
||||||
category: 'Cloud Services',
|
category: 'Cloud Services',
|
||||||
color: 'data',
|
color: 'data',
|
||||||
initialize: function () {
|
initialize() {
|
||||||
var internal = this._internal;
|
const internal = this._internal;
|
||||||
|
|
||||||
internal.userProperties = {};
|
internal.userProperties = {};
|
||||||
},
|
},
|
||||||
getInspectInfo() {},
|
|
||||||
outputs: {
|
outputs: {
|
||||||
success: {
|
success: {
|
||||||
type: 'signal',
|
type: 'signal',
|
||||||
@@ -30,7 +27,7 @@ var SignUpNodeDefinition = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
displayName: 'Error',
|
displayName: 'Error',
|
||||||
group: 'Error',
|
group: 'Error',
|
||||||
getter: function () {
|
getter() {
|
||||||
return this._internal.error;
|
return this._internal.error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,7 +36,7 @@ var SignUpNodeDefinition = {
|
|||||||
signup: {
|
signup: {
|
||||||
displayName: 'Do',
|
displayName: 'Do',
|
||||||
group: 'Actions',
|
group: 'Actions',
|
||||||
valueChangedToTrue: function () {
|
valueChangedToTrue() {
|
||||||
this.scheduleSignUp();
|
this.scheduleSignUp();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -47,7 +44,7 @@ var SignUpNodeDefinition = {
|
|||||||
displayName: 'Username',
|
displayName: 'Username',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
group: 'General',
|
group: 'General',
|
||||||
set: function (value) {
|
set(value) {
|
||||||
this._internal.username = value;
|
this._internal.username = value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -55,7 +52,7 @@ var SignUpNodeDefinition = {
|
|||||||
displayName: 'Password',
|
displayName: 'Password',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
group: 'General',
|
group: 'General',
|
||||||
set: function (value) {
|
set(value) {
|
||||||
this._internal.password = value;
|
this._internal.password = value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -63,13 +60,13 @@ var SignUpNodeDefinition = {
|
|||||||
displayName: 'Email',
|
displayName: 'Email',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
group: 'General',
|
group: 'General',
|
||||||
set: function (value) {
|
set(value) {
|
||||||
this._internal.email = value;
|
this._internal.email = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setError: function (err) {
|
setError(err) {
|
||||||
this._internal.error = err;
|
this._internal.error = err;
|
||||||
this.flagOutputDirty('error');
|
this.flagOutputDirty('error');
|
||||||
this.sendSignalOnOutput('failure');
|
this.sendSignalOnOutput('failure');
|
||||||
@@ -86,7 +83,7 @@ var SignUpNodeDefinition = {
|
|||||||
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
|
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scheduleSignUp: function () {
|
scheduleSignUp() {
|
||||||
const internal = this._internal;
|
const internal = this._internal;
|
||||||
|
|
||||||
if (this.signUpScheduled === true) return;
|
if (this.signUpScheduled === true) return;
|
||||||
@@ -109,23 +106,24 @@ var SignUpNodeDefinition = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setUserProperty: function (name, value) {
|
setUserProperty(name, value) {
|
||||||
this._internal.userProperties[name] = value;
|
this._internal.userProperties[name] = value;
|
||||||
},
|
},
|
||||||
registerInputIfNeeded: function (name) {
|
registerInputIfNeeded(name) {
|
||||||
if (this.hasInput(name)) {
|
if (this.hasInput(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.startsWith('prop-'))
|
if (name.startsWith('prop-')) {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setUserProperty.bind(this, name.substring('prop-'.length))
|
set: this.setUserProperty.bind(this, name.substring('prop-'.length))
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function updatePorts(nodeId, parameters, editorConnection, systemCollections) {
|
function updatePorts(nodeId, _parameters, editorConnection, systemCollections) {
|
||||||
var ports = [];
|
var ports = [];
|
||||||
|
|
||||||
if (systemCollections) {
|
if (systemCollections) {
|
||||||
@@ -169,7 +167,7 @@ function updatePorts(nodeId, parameters, editorConnection, systemCollections) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
node: SignUpNodeDefinition,
|
node: SignUpNodeDefinition,
|
||||||
setup: function (context, graphModel) {
|
setup(context, graphModel) {
|
||||||
if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
|
if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -177,7 +175,7 @@ module.exports = {
|
|||||||
function _managePortsForNode(node) {
|
function _managePortsForNode(node) {
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections'));
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections'));
|
||||||
|
|
||||||
node.on('parameterUpdated', function (event) {
|
node.on('parameterUpdated', function (_event) {
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections'));
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const ColumnsNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize() {
|
initialize() { this.props.attrs = {};
|
||||||
this.props.layoutString = '1 2 1';
|
this.props.layoutString = '1 2 1';
|
||||||
this.props.minWidth = 0;
|
this.props.minWidth = 0;
|
||||||
this.props.marginX = 16;
|
this.props.marginX = 16;
|
||||||
@@ -62,6 +62,16 @@ const ColumnsNode = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const GroupNode = {
|
|||||||
groupPriority: ['General', 'Style', 'Events', 'Mounted', 'Hover Events', 'Pointer Events', 'Focus', 'Scroll']
|
groupPriority: ['General', 'Style', 'Events', 'Mounted', 'Hover Events', 'Pointer Events', 'Focus', 'Scroll']
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this._internal = {
|
this._internal = {
|
||||||
scrollElementDuration: 500,
|
scrollElementDuration: 500,
|
||||||
scrollIndexDuration: 500,
|
scrollIndexDuration: 500,
|
||||||
@@ -143,6 +144,16 @@ const GroupNode = {
|
|||||||
valueChangedToTrue() {
|
valueChangedToTrue() {
|
||||||
this.context.setNodeFocused(this, true);
|
this.context.setNodeFocused(this, true);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputProps: {
|
inputProps: {
|
||||||
|
|||||||