1 Commits

Author SHA1 Message Date
Eric Tuvesson
1190dbdf89 feat: Slider add children to thumb
I am not super happy with how this works as the child is inside the thumb div. Ideally it should be moving around the thumb, but then that might be a tooltip lib?
2024-05-21 16:47:16 +02:00
132 changed files with 1157 additions and 2049 deletions

View File

@@ -1,4 +1,4 @@
name: Build fluxscape-editor name: Build noodl-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: fluxscape-editor-${{ matrix.platform }}-${{ github.head_ref }}-${{ github.sha }} name: noodl-editor-${{ matrix.platform }}-${{ github.head_ref }}-${{ github.sha }}
path: publish path: publish
retention-days: '12' retention-days: "12"

View File

@@ -1,4 +1,4 @@
name: Test fluxscape-editor name: Test noodl-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

View File

@@ -1,21 +1,21 @@
# Fluxscape # Noodl
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. [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.
## Documentation ## Documentation
Documentation for how to use Fluxscape can be found here: Documentation for how to use Noodl can be found here:
[Fluxscape Documentation](https://docs.fluxscape.io) [https://noodlapp.github.io/noodl-docs/](https://noodlapp.github.io/noodl-docs/)
## Community ## Community
Main support channel is Discord: [Fluxscape Discord](https://discord.gg/fXNW9EXa6A) Main support channel is Discord: [https://www.noodl.net/community](https://www.noodl.net/community)
## Download releases ## Download releases
Pre-built binaries can be [downloaded from Github](https://github.com/fluxscape/fluxscape/releases) Pre-built binaries can be [downloaded from Github](https://github.com/noodlapp/noodl/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://docs.fluxscape.io/docs/guides/collaboration/migrating-from-noodl-hosted-git/) - [Migrating the project files and workspaces to a Git provider](https://noodlapp.github.io/noodl-docs/docs/guides/collaboration/migrating-from-noodl-hosted-git)
- [Migrate backend and database](https://docs.fluxscape.io/docs/guides/deploy/using-an-external-backend/#migrating-from-a-noodl-cloud-service) - [Migrate backend and database](https://noodlapp.github.io/noodl-docs/docs/guides/deploy/using-an-external-backend#migrating-from-a-noodl-cloud-service)
- [Self-host frontend](https://docs.fluxscape.io/docs/guides/deploy/hosting-frontend/) - [Self-host frontend](https://noodlapp.github.io/noodl-docs/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/fluxscape/
# Install all dependencies # Install all dependencies
$ npm install $ npm install
# 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) # 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)
$ npm start $ npm start
# 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) # 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)
# 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 Fluxscape Editor test runner # Start Noodl Editor test runner
$ npm run test:editor $ npm run test:editor
``` ```
## Licenses ## Licenses
This repository contains two different licenses for different parts of the Fluxscape platform. This repository contains two different licenses for different parts of the Noodl platform.
- Components related to the editor, used to edit Fluxscape projects, are under GPLv3 - Components related to the editor, used to edit Noodl projects, are under GPLv3
- Components related to the end applications, used by the applications Fluxscape deploys, are under MIT - Components related to the end applications, used by the applications Noodl deploys, are under MIT
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. 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.
Packaged licensed under MIT: Packaged licensed under MIT:
- `noodl-runtime` - `noodl-runtime`

347
package-lock.json generated
View File

@@ -2849,9 +2849,9 @@
} }
}, },
"node_modules/@electron/remote": { "node_modules/@electron/remote": {
"version": "2.1.2", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz", "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==", "integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
"peerDependencies": { "peerDependencies": {
"electron": ">= 13.0.0" "electron": ">= 13.0.0"
} }
@@ -25220,13 +25220,13 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "31.3.1", "version": "28.1.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
"integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==", "integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
"@types/node": "^20.9.0", "@types/node": "^18.11.18",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
}, },
"bin": { "bin": {
@@ -25452,14 +25452,6 @@
"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,
@@ -27211,10 +27203,6 @@
"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,
@@ -35455,6 +35443,10 @@
"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",
@@ -43199,11 +43191,6 @@
"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,
@@ -48980,10 +48967,9 @@
"dev": true "dev": true
}, },
"packages/noodl-editor": { "packages/noodl-editor": {
"name": "fluxscape-editor", "version": "1.0.0",
"version": "1.2.0",
"dependencies": { "dependencies": {
"@electron/remote": "^2.1.2", "@electron/remote": "^2.1.1",
"@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",
@@ -49039,7 +49025,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": "31.3.1", "electron": "28.1.4",
"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",
@@ -53389,9 +53375,9 @@
} }
}, },
"@electron/remote": { "@electron/remote": {
"version": "2.1.2", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz", "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==", "integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
"requires": {} "requires": {}
}, },
"@electron/universal": { "@electron/universal": {
@@ -74071,23 +74057,13 @@
} }
}, },
"electron": { "electron": {
"version": "31.3.1", "version": "28.1.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
"integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==", "integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
"requires": { "requires": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
"@types/node": "^20.9.0", "@types/node": "^18.11.18",
"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": {
@@ -75432,144 +75408,6 @@
"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,
@@ -81145,6 +80983,144 @@
"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": {
@@ -86427,11 +86403,6 @@
"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

View File

@@ -2,8 +2,8 @@
"private": true, "private": true,
"name": "@noodl/repo", "name": "@noodl/repo",
"description": "Low-code for when experience matter", "description": "Low-code for when experience matter",
"author": "Fluxscape <contact@fluxcsape.io>", "author": "Noodl <info@noodl.net>",
"homepage": "https://fluxscape.io", "homepage": "https://noodl.net",
"version": "1.0.0", "version": "1.0.0",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
@@ -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 fluxscape-editor -- npm run ci:build", "ci:build:editor": "lerna exec --scope noodl-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 fluxscape-editor --stream", "start:editor": "lerna run start --scope noodl-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",

View File

@@ -9,11 +9,6 @@
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);
} }

View File

@@ -42,7 +42,6 @@ export interface BaseDialogProps extends UnsafeStyleProps {
isVisible?: boolean; isVisible?: boolean;
hasBackdrop?: boolean; hasBackdrop?: boolean;
hasArrow?: boolean; hasArrow?: boolean;
alwaysMounted?: boolean;
children?: Slot; children?: Slot;
@@ -70,7 +69,6 @@ export function CoreBaseDialog({
isVisible, isVisible,
hasBackdrop, hasBackdrop,
hasArrow, hasArrow,
alwaysMounted,
children, children,
@@ -263,7 +261,7 @@ export function CoreBaseDialog({
} }
}, [background]); }, [background]);
if (!isVisible && !alwaysMounted) return null; if (!isVisible) return null;
return ( return (
<div <div
@@ -272,7 +270,6 @@ 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}

View File

@@ -11,7 +11,7 @@ import css from './Tooltip.module.scss';
export interface TooltipProps extends UnsafeStyleProps { export interface TooltipProps extends UnsafeStyleProps {
content: SingleSlot; content: SingleSlot;
fineType?: string | string[]; fineType?: string;
children: Slot; children: Slot;
showAfterMs?: number; showAfterMs?: number;
@@ -79,17 +79,9 @@ export function Tooltip({
{fineType && ( {fineType && (
<div className={css['FineType']}> <div className={css['FineType']}>
{Array.isArray(fineType) ? (
fineType.map((x) => (
<Label size={LabelSize.Small} variant={TextType.Secondary}>
{x}
</Label>
))
) : (
<Label size={LabelSize.Small} variant={TextType.Secondary}> <Label size={LabelSize.Small} variant={TextType.Secondary}>
{fineType} {fineType}
</Label> </Label>
)}
</div> </div>
)} )}
</BaseDialog> </BaseDialog>

View File

@@ -1,10 +1,10 @@
{ {
"name": "fluxscape-editor", "name": "noodl-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.2.0", "version": "1.0.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.2", "@electron/remote": "^2.1.1",
"@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": "31.3.1", "electron": "28.1.4",
"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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 175 B

View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 408 B

View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 220 B

View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 360 B

View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

View File

@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Fluxscape</title> <title>Noodl</title>
<link href="../assets/lib/fontawesome/css/font-awesome.min.css" rel="stylesheet" /> <link href="../assets/lib/fontawesome/css/font-awesome.min.css" rel="stylesheet" />
<link href="../assets/css/style.css" rel="stylesheet" /> <link href="../assets/css/style.css" rel="stylesheet" />

View File

@@ -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 { WarningRef, WarningsModel } from './models/warningsmodel'; import { 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: WarningRef = { const ref = {
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: WarningRef = { const ref = {
component: ProjectModel.instance.getComponentWithName(content.componentName), component: ProjectModel.instance.getComponentWithName(content.componentName),
node: ProjectModel.instance.findNodeWithId(content.nodeId) node: ProjectModel.instance.findNodeWithId(content.nodeId)
}; };

View File

@@ -2,11 +2,6 @@ import { Keybinding } from '@noodl-utils/keyboard/Keybinding';
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode'; import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
export namespace Keybindings { export namespace Keybindings {
export const SEARCH = new Keybinding(KeyMod.CtrlCmd, KeyCode.KEY_F);
export const CLOUD_SERVICE_OPEN_DASHBOARD = new Keybinding(KeyMod.CtrlCmd, KeyMod.Shift, KeyCode.KEY_P);
export const CLOUD_SERVICE_OPEN_DASHBOARD_BROWSER = new Keybinding(KeyMod.CtrlCmd, KeyCode.KEY_P);
export const REFRESH_PREVIEW = new Keybinding(KeyMod.CtrlCmd, KeyCode.KEY_R); export const REFRESH_PREVIEW = new Keybinding(KeyMod.CtrlCmd, KeyCode.KEY_R);
export const OPEN_DEVTOOLS = new Keybinding(KeyMod.CtrlCmd, KeyCode.KEY_D); export const OPEN_DEVTOOLS = new Keybinding(KeyMod.CtrlCmd, KeyCode.KEY_D);
export const OPEN_CLOUD_DEVTOOLS = new Keybinding(KeyMod.CtrlCmd, KeyMod.Shift, KeyCode.KEY_R); export const OPEN_CLOUD_DEVTOOLS = new Keybinding(KeyMod.CtrlCmd, KeyMod.Shift, KeyCode.KEY_R);

View File

@@ -1,142 +0,0 @@
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;
}

View File

@@ -1 +0,0 @@
export * from './NodeReferencesContext';

View File

@@ -1,72 +0,0 @@
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
};
}
}

View File

@@ -1,13 +1,11 @@
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 | CloudService.Command; type IFrameCommand = Notify.Command | UploadAwsS3.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) {

View File

@@ -57,13 +57,10 @@ 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 const functions = functionRequestNodes.map((r) => {
.map((r) => {
const component = r.owner.owner; const component = r.owner.owner;
return component.fullName; 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',

View File

@@ -1,5 +1,5 @@
import { NodeGraphNode } from '@noodl-models/nodegraphmodel'; import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { type Warning, WarningsModel } from '@noodl-models/warningsmodel'; import { 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: Warning = const 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",

View File

@@ -458,11 +458,11 @@ export class VariantModel extends Model {
} }
); );
} else if (c.type === 'defaultStateTransition') { } else if (c.type === 'defaultStateTransition') {
const state = var 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;
const stateName = state !== undefined ? state.label : c.state; var 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 },

View File

@@ -72,7 +72,7 @@ export class NodeGraphNode extends Model {
metadata?: Record<string, any>; metadata?: Record<string, any>;
private _variant: TSFixme; private _variant: TSFixme;
_label: TSFixme; private _label: TSFixme;
private _type: TSFixme; private _type: TSFixme;
private _ports: TSFixme; private _ports: TSFixme;

View File

@@ -1167,28 +1167,6 @@ 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;

View File

@@ -1,42 +1,9 @@
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
@@ -47,7 +14,7 @@ interface Warnings {
[node_connection_id: string]: { [node_connection_id: string]: {
[warningKey: string]: { [warningKey: string]: {
ref: TSFixme; ref: TSFixme;
warning: Warning; warning: TSFixme;
}; };
}; };
}; };
@@ -57,7 +24,7 @@ export class WarningsModel extends Model {
public static instance = new WarningsModel(); public static instance = new WarningsModel();
private warnings: Warnings = {}; private warnings: Warnings = {};
private notifyChangedScheduled = false; private notifyChangedScheduled: boolean = false;
constructor() { constructor() {
super(); super();
@@ -68,12 +35,10 @@ export class WarningsModel extends Model {
}); });
} }
public setWarning(ref: WarningRef, warning: Warning) { public setWarning(ref, warning) {
const w = this.getWarningsForRef(ref, warning !== undefined); var w = this.getWarningsForRef(ref, warning !== undefined);
if (!warning) { if (!warning) {
if (w) { if (w) delete w[ref.key];
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 };
@@ -82,34 +47,31 @@ export class WarningsModel extends Model {
this.scheduleNotifyChanged(); this.scheduleNotifyChanged();
} }
public clearWarningsForRef(ref: WarningRef) { public clearWarningsForRef(ref) {
const w = this.getWarningsForRef(ref); var w = this.getWarningsForRef(ref);
if (!w) return; if (!w) return;
for (const i in w) { for (var i in w) delete w[i];
delete w[i];
}
this.scheduleNotifyChanged(); this.scheduleNotifyChanged();
} }
public clearAllWarningsForComponent(component: ComponentModel) { public clearAllWarningsForComponent(component) {
const warnings = this.warnings[component.name]; const warnings = this.warnings[component.name];
if (!warnings) return; if (!warnings) return;
for (const i in warnings) { for (let i in warnings) delete warnings[i];
delete warnings[i];
}
this.scheduleNotifyChanged(); this.scheduleNotifyChanged();
} }
public clearWarningsForRefMatching(matchFn: (ref: TSFixme) => boolean) { public clearWarningsForRefMatching(matchCb) {
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 (matchFn(w.ref)) { if (matchCb(w.ref)) {
delete ws[key]; delete ws[key];
} }
} }
@@ -125,14 +87,15 @@ export class WarningsModel extends Model {
this.scheduleNotifyChanged(); this.scheduleNotifyChanged();
} }
public getWarnings(ref: WarningRef) { public getWarnings(ref) {
const w = this.getWarningsForRef(ref); var 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
const messages = []; var messages = [];
for (const k in w) { for (var k in w) {
if (w[k].warning) messages.push(w[k].warning.message); if (w[k].warning) messages.push(w[k].warning.message);
} }
@@ -143,13 +106,13 @@ export class WarningsModel extends Model {
} }
public forEachWarningInComponent(c, callback, args) { public forEachWarningInComponent(c, callback, args) {
const cw = this.warnings[c ? c.name : '/']; var cw = this.warnings[c ? c.name : '/'];
if (!cw) return; if (!cw) return;
for (const ref in cw) { for (var ref in cw) {
const ws = cw[ref]; var ws = cw[ref];
for (const w in ws) { for (var 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]);
} }
@@ -158,7 +121,7 @@ export class WarningsModel extends Model {
} }
public getAllWarningsForComponent(c, args) { public getAllWarningsForComponent(c, args) {
const warnings = []; var warnings = [];
this.forEachWarningInComponent( this.forEachWarningInComponent(
c, c,
function (warning) { function (warning) {
@@ -189,15 +152,15 @@ export class WarningsModel extends Model {
} }
public getTotalNumberOfWarnings(args) { public getTotalNumberOfWarnings(args) {
let total = 0; var total = 0;
for (const key in this.warnings) { for (var 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) {
let total = 0; var 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++;
}); });
@@ -205,16 +168,16 @@ export class WarningsModel extends Model {
} }
public forEachWarning(callback: (c: string, ref, key, warning) => void) { public forEachWarning(callback: (c: string, ref, key, warning) => void) {
const w = this.warnings; var w = this.warnings;
for (const c in w) { for (const c in w) {
// Loop over all components // Loop over all components
const _w = w[c]; var _w = w[c];
for (const ref in _w) { for (const ref in _w) {
// Loop over all refs // Loop over all refs
const __w = _w[ref]; var __w = _w[ref];
for (const key in __w) { for (const key in __w) {
// Loop over all keys // Loop over all keys
const warning = __w[key]; var warning = __w[key];
callback(c, ref, key, warning); callback(c, ref, key, warning);
} }
@@ -232,12 +195,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: WarningRef, create?) { private getWarningsForRef(ref, create?) {
const componentName = ref.component ? ref.component.name : '/'; var componentName = ref.component ? ref.component.name : '/';
if (!this.warnings[componentName]) this.warnings[componentName] = {}; if (!this.warnings[componentName]) this.warnings[componentName] = {};
const cw = this.warnings[componentName]; var cw = this.warnings[componentName];
let key; var 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 =
@@ -257,8 +220,7 @@ 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() {
// eslint-disable-next-line @typescript-eslint/no-this-alias var _this = this;
const _this = this;
if (this.notifyChangedScheduled) return; if (this.notifyChangedScheduled) return;
this.notifyChangedScheduled = true; this.notifyChangedScheduled = true;

View File

@@ -1,6 +1,4 @@
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';
@@ -45,6 +43,7 @@ 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');
@@ -224,7 +223,6 @@ export function EditorPage({ route }: EditorPageProps) {
return ( return (
<NodeGraphContextProvider> <NodeGraphContextProvider>
<NodeReferencesContextProvider>
<ProjectDesignTokenContextProvider> <ProjectDesignTokenContextProvider>
<PluginContextProvider> <PluginContextProvider>
<BaseWindow> <BaseWindow>
@@ -247,7 +245,6 @@ export function EditorPage({ route }: EditorPageProps) {
</BaseWindow> </BaseWindow>
</PluginContextProvider> </PluginContextProvider>
</ProjectDesignTokenContextProvider> </ProjectDesignTokenContextProvider>
</NodeReferencesContextProvider>
</NodeGraphContextProvider> </NodeGraphContextProvider>
); );
} }

View File

@@ -1,6 +1,7 @@
import { AppRegistry } from '@noodl-models/app_registry'; import { AppRegistry } from '@noodl-models/app_registry';
import { SidebarModel } from '@noodl-models/sidebar'; import { SidebarModel } from '@noodl-models/sidebar';
import { Keybindings } from '@noodl-constants/Keybindings'; import { Keybinding } from '@noodl-utils/keyboard/Keybinding';
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
import { IconName } from '@noodl-core-ui/components/common/Icon'; import { IconName } from '@noodl-core-ui/components/common/Icon';
@@ -68,7 +69,7 @@ export function installSidePanel({ isLesson }: SetupEditorOptions) {
SidebarModel.instance.register({ SidebarModel.instance.register({
id: 'search', id: 'search',
name: 'Search', name: 'Search',
fineType: Keybindings.SEARCH.label, fineType: new Keybinding(KeyMod.CtrlCmd, KeyCode.KEY_F).label,
order: 2, order: 2,
icon: IconName.Search, icon: IconName.Search,
panel: SearchPanel panel: SearchPanel

View File

@@ -16,7 +16,6 @@ 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;
@@ -268,15 +267,12 @@ export class LocalProjectsModel extends Model {
}); });
} }
/** isGitProject(project: ProjectModel): boolean {
* Check if this project is in a git repository. // TODO: check if there's is git in any parent folder too
*
* @param project // Check if the git folder exists.
* @returns const gitPath = filesystem.join(project._retainedProjectDirectory, '.git');
*/ 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) {

View File

@@ -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', 'nodelibrary.json']; const ignoreFiles = ['.DS_Store', '.gitignore', '.gitattributes', 'project.json', 'Dockerfile'];
// Copy everything from the project folder // Copy everything from the project folder
if (!projectPath) { if (!projectPath) {

View File

@@ -67,14 +67,6 @@ 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;

View File

@@ -41,7 +41,7 @@ export class HtmlProcessor {
baseUrl = baseUrl + '/'; baseUrl = baseUrl + '/';
} }
const title = parameters.title || settings.htmlTitle || 'Fluxscape Viewer'; const title = parameters.title || settings.htmlTitle || 'Noodl Viewer';
let headCode = settings.headCode || ''; let headCode = settings.headCode || '';
if (parameters.headCode) { if (parameters.headCode) {

View File

@@ -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 this.getDocsEndpoint() || 'https://docs.fluxscape.io'; return 'https://docs.noodl.net';
} }
constructor(private readonly getDocsEndpoint: () => string) {} constructor(private readonly getDocsEndpoint: () => string) {}

View File

@@ -11,14 +11,6 @@ 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;

View File

@@ -13,7 +13,6 @@ 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(
@@ -120,20 +119,6 @@ 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;
});
}); });
}); });
} }
@@ -144,20 +129,10 @@ 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);
} }
} }
} }

View File

@@ -1,32 +1,22 @@
import type { ComponentModel } from '@noodl-models/componentmodel'; const { ProjectModel } = require('../models/projectmodel');
import type { NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { ProjectModel } from '../models/projectmodel'; function matchStrings(string1, string2) {
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: NodeGraphNode, searchTerms: string, component: ComponentModel) { function searchInNodeRecursive(node, searchTerms, component) {
let results: SearchResult[] = []; var results = [];
let matchLabel: string | null = null; var matchLabel = 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 (node.id === searchTerms) { } else if (matchStrings(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 {
const parameterNames = Object.keys(node.parameters); let parameterNames = Object.keys(node.parameters);
for (const parameterNameIndex in parameterNames) { for (const parameterNameIndex in parameterNames) {
const parameterName = parameterNames[parameterNameIndex]; const parameterName = parameterNames[parameterNameIndex];
@@ -35,7 +25,7 @@ function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, compone
matchStrings(node.parameters[parameterName], searchTerms) matchStrings(node.parameters[parameterName], searchTerms)
) { ) {
let displayLabel = parameterName; let displayLabel = parameterName;
const connectionPort = node.type.ports?.find((port) => port.name === parameterName); let connectionPort = node.type.ports?.find((port) => port.name === parameterName);
if (connectionPort) { if (connectionPort) {
displayLabel = connectionPort.displayName; displayLabel = connectionPort.displayName;
} }
@@ -61,9 +51,9 @@ function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, compone
} }
if (matchLabel === null) { if (matchLabel === null) {
const ports = node.dynamicports; var ports = node.dynamicports;
for (let i = 0; i < ports.length; ++i) { for (i = 0; i < ports.length; ++i) {
const port = ports[i]; var 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;
@@ -72,9 +62,9 @@ function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, compone
} }
if (matchLabel === null) { if (matchLabel === null) {
const ports = node.ports; var ports = node.ports;
for (let i = 0; i < ports.length; ++i) { for (i = 0; i < ports.length; ++i) {
const port = ports[i]; var 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;
@@ -93,17 +83,17 @@ function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, compone
}); });
} }
for (let i = 0; i < node.children.length; ++i) { for (i = 0; i < node.children.length; ++i) {
const child = node.children[i]; var child = node.children[i];
const childResults = searchInNodeRecursive(child, searchTerms, component); var childResults = searchInNodeRecursive(child, searchTerms, component);
results = results.concat(childResults); results = results.concat(childResults);
} }
return results; return results;
} }
function searchInComponent(component: ComponentModel, searchTerms: string) { function searchInComponent(component, searchTerms) {
let results: SearchResult[] = []; var results = [];
if (matchStrings(component.displayName, searchTerms)) { if (matchStrings(component.displayName, searchTerms)) {
results.push({ results.push({
componentTarget: component, componentTarget: component,
@@ -112,14 +102,14 @@ function searchInComponent(component: ComponentModel, searchTerms: string) {
}); });
} }
for (let i = 0; i < component.graph.roots.length; ++i) { for (var i = 0; i < component.graph.roots.length; ++i) {
const node = component.graph.roots[i]; var node = component.graph.roots[i];
const nodeResults = searchInNodeRecursive(node, searchTerms, component); var nodeResults = searchInNodeRecursive(node, searchTerms, component);
results = results.concat(nodeResults); results = results.concat(nodeResults);
} }
if (component.graph.commentsModel.comments) { if (component.graph.commentsModel.comments) {
for (let i = 0; i < component.graph.commentsModel.comments.length; ++i) { for (var 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({
@@ -142,17 +132,17 @@ function searchInComponent(component: ComponentModel, searchTerms: string) {
} }
} }
export function performSearch(searchTerms: string) { export function performSearch(searchTerms) {
const results: ReturnType<typeof searchInComponent>[] = []; var results = [];
const root = ProjectModel.instance.getRootNode(); var root = ProjectModel.instance.getRootNode();
if (root === undefined) return; if (root === undefined) return;
const components = ProjectModel.instance.components; var components = ProjectModel.instance.components;
for (let i = 0; i < components.length; ++i) { for (var i = 0; i < components.length; ++i) {
const component = components[i]; var component = components[i];
const componentResults = searchInComponent(component, searchTerms); var 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) {

View File

@@ -211,7 +211,7 @@ export default function Clippy() {
aiAssistantModel.removeActivity(id); aiAssistantModel.removeActivity(id);
} }
const initialPlaceholder = isInputOpen ? 'Select (or type) a command below' : 'Ask FluxScape AI'; const initialPlaceholder = isInputOpen ? 'Select (or type) a command below' : 'Ask Noodl 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 FluxScape AI to use the Limited Beta in If you dont have an API key with GPT-4 access, you can set the Noodl AI to use the Limited Beta in the
the editor settings. 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 FluxScape AI. If features fewer commands and a less capable AI. Get You are running the limited beta of Noodl AI. If features fewer commands and a less capable AI. Get full
full beta access by bringing your own GPT-4 API key. beta access by bringing your own GPT-4 API key.
</Text> </Text>
<PrimaryButton <PrimaryButton

View File

@@ -1,7 +1,5 @@
import React, { RefObject } from 'react'; import { usePluginContext } from '@noodl-contexts/PluginContext';
import { platform } from '@noodl/platform'; import React, { RefObject, useEffect, useRef } from 'react';
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';
@@ -56,7 +54,6 @@ export function DeployPopup(props: DeployPopupProps) {
onClose={props.onClose} onClose={props.onClose}
hasArrow hasArrow
isLockingScroll isLockingScroll
alwaysMounted
> >
<DeployPopupChild /> <DeployPopupChild />
</BaseDialog> </BaseDialog>
@@ -65,21 +62,6 @@ export function DeployPopup(props: DeployPopupProps) {
} }
function FluxscapeDeployTab() { function FluxscapeDeployTab() {
const params = { // Preview URL: 'http://192.168.0.33:8574/'
version: platform.getVersion() return <iframe src="https://portal.fluxscape.io" style={{ width: "100%", height: "50vh", borderStyle: "none" }} />;
};
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' }}
/>
);
} }

View File

@@ -1,16 +1,13 @@
import classNames from 'classnames'; import classNames from 'classnames';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { Keybindings } from '@noodl-constants/Keybindings';
import { Environment } from '@noodl-models/CloudServices'; import { Environment } from '@noodl-models/CloudServices';
import ParseDashboardServer from '@noodl-utils/parsedashboardserver'; import ParseDashboardServer from '@noodl-utils/parsedashboardserver';
import { Icon, IconName, IconSize } from '@noodl-core-ui/components/common/Icon'; import { Icon, IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
import { IconButton, IconButtonState, IconButtonVariant } from '@noodl-core-ui/components/inputs/IconButton'; import { IconButton, IconButtonState, IconButtonVariant } from '@noodl-core-ui/components/inputs/IconButton';
import { PrimaryButton, PrimaryButtonSize, PrimaryButtonVariant } from '@noodl-core-ui/components/inputs/PrimaryButton'; import { PrimaryButton, PrimaryButtonSize, PrimaryButtonVariant } from '@noodl-core-ui/components/inputs/PrimaryButton';
import { DialogRenderDirection } from '@noodl-core-ui/components/layout/BaseDialog';
import { Collapsible } from '@noodl-core-ui/components/layout/Collapsible'; import { Collapsible } from '@noodl-core-ui/components/layout/Collapsible';
import { Tooltip } from '@noodl-core-ui/components/popups/Tooltip';
import { Label, LabelSpacingSize } from '@noodl-core-ui/components/typography/Label'; import { Label, LabelSpacingSize } from '@noodl-core-ui/components/typography/Label';
import { Text, TextType } from '@noodl-core-ui/components/typography/Text'; import { Text, TextType } from '@noodl-core-ui/components/typography/Text';
@@ -121,21 +118,12 @@ export function CloudServiceCard({
</div> </div>
{isEditorEnvironment && ( {isEditorEnvironment && (
<Tooltip
content="Open the Parse Dashboard"
fineType={[
`In Window: ${Keybindings.CLOUD_SERVICE_OPEN_DASHBOARD.label}`,
`In Browser: ${Keybindings.CLOUD_SERVICE_OPEN_DASHBOARD_BROWSER.label}`
]}
renderDirection={DialogRenderDirection.Below}
>
<PrimaryButton <PrimaryButton
label="Open dashboard" label="Open dashboard"
size={PrimaryButtonSize.Small} size={PrimaryButtonSize.Small}
onClick={onDashboardClicked} onClick={onDashboardClicked}
isGrowing isGrowing
/> />
</Tooltip>
)} )}
</div> </div>
</div> </div>

View File

@@ -6,6 +6,8 @@ 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';

View File

@@ -44,7 +44,7 @@ export function OpenAiSection() {
} }
return ( return (
<CollapsableSection title="FluxScape AI (Beta)"> <CollapsableSection title="Noodl 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>FluxScape AI is currently disabled.</Text> <Text>Noodl 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>
FluxScape AI docs Noodl AI docs
</Title> </Title>
<Text hasBottomSpacing>See setup instructions and guides for how to use FluxScape AI on our docs.</Text> <Text hasBottomSpacing>See setup instructions and guides for how to use Noodl 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.fluxscape.io/docs/getting-started/noodl-ai'); platform.openExternal('https://docs.noodl.net/#/docs/getting-started/noodl-ai/');
}} }}
/> />
</Box> </Box>

View File

@@ -1,12 +1,14 @@
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, { useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { INodeColorScheme } from '@noodl-types/nodeTypes'; import { INodeColorScheme } from '@noodl-types/nodeTypes';
import { NodeLibrary } from '@noodl-models/nodelibrary'; import { ComponentModel } from '@noodl-models/componentmodel';
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';
@@ -24,17 +26,113 @@ 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 { nodeReferences } = useNodeReferencesContext(); const [result] = useNodeReferences();
const nodeLibraryLoaded = useNodeLibraryLoaded(); const nodeLibraryLoaded = useNodeLibraryLoaded();
useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID); useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID);
function searchFilter(x: NodeReference) { function searchFilter(x: ResultItem) {
if (x.displayName.toLowerCase().includes(searchTerm)) { if (x.displayName.toLowerCase().includes(searchTerm)) {
return true; return true;
} }
@@ -46,7 +144,7 @@ export function NodeReferencesPanel() {
return false; return false;
} }
let filteredResult = nodeReferences.filter(searchFilter); let filteredResult = result.filter(searchFilter);
if (!includeCoreNodes) { if (!includeCoreNodes) {
filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/')); filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/'));
} }
@@ -87,7 +185,7 @@ export function NodeReferencesPanel() {
} }
interface ItemProps { interface ItemProps {
entry: NodeReference; entry: ResultItem;
} }
function Item({ entry }: ItemProps) { function Item({ entry }: ItemProps) {
@@ -147,8 +245,8 @@ function Item({ entry }: ItemProps) {
} }
interface ItemReferenceProps { interface ItemReferenceProps {
entry: NodeReference; entry: ResultItem;
referenace: NodeReference['referenaces'][0]; referenace: ResultItem['referenaces'][0];
colors: INodeColorScheme; colors: INodeColorScheme;
} }

View File

@@ -199,7 +199,6 @@ 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);
@@ -207,18 +206,12 @@ export function VersionControlPanel() {
setGit(gitClient); setGit(gitClient);
} }
const isGitProject = git === null ? LocalProjectsModel.instance.isGitProject(ProjectModel.instance) : true;
useEffect(() => { useEffect(() => {
LocalProjectsModel.instance
.isGitProject(ProjectModel.instance)
.then(async (isGitProject) => {
if (isGitProject) { if (isGitProject) {
await createGit(); createGit();
setState('loaded');
} else {
setState('not-git');
} }
}); }, [isGitProject]);
}, []);
async function setupGit() { async function setupGit() {
const gitClient = new Git(mergeProject); const gitClient = new Git(mergeProject);
@@ -227,7 +220,7 @@ export function VersionControlPanel() {
setGit(gitClient); setGit(gitClient);
} }
if (git === null && state === 'not-git') { if (git === null && !isGitProject) {
return ( return (
<BasePanel isFill title="Version Control"> <BasePanel isFill title="Version Control">
<Box hasXSpacing hasYSpacing> <Box hasXSpacing hasYSpacing>

View File

@@ -6,13 +6,11 @@ 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:
@@ -126,10 +124,7 @@ async function getMergeDiff(repositoryPath: string, commit: Commit, refToDiffTo:
} }
async function getProjectFile(commit: Commit) { async function getProjectFile(commit: Commit) {
const projectFilePath = getProjectFilePath(commit.repositoryDir, ProjectModel.instance._retainedProjectDirectory); const projectContent = JSON.parse(await commit.getFileAsString('project.json'));
const projectContentRaw = await commit.getFileAsString(projectFilePath);
const projectContent = JSON.parse(projectContentRaw);
applyPatches(projectContent); applyPatches(projectContent);
return projectContent; return projectContent;
} }

View File

@@ -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.endsWith('project.json')) || []; const files = (fileChanges || [])?.filter((f) => f.path !== '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) : [];

View File

@@ -1,22 +1,19 @@
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 { ProjectModel } from '../../../../models/projectmodel';
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
import { FileStatusKind } from '@noodl/git/src/core/models/status';
import { IconName } from '@noodl-core-ui/components/common/Icon'; import { IconName } from '@noodl-core-ui/components/common/Icon';
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem'; import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
import { FeedbackType } from '@noodl-constants/FeedbackType';
import { ProjectModel } from '../../../../models/projectmodel'; import { getCommit } from '@noodl/git/src/core/logs';
export interface ProjectLocalDiff extends ProjectDiff{ 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
@@ -87,22 +84,9 @@ export function getFileStatusIconProps(status: FileStatusKind): Partial<ListItem
} }
} }
export function getProjectFilePath(repositoryPath: string, projectPath: string) { export async function doLocalDiff(repositoryPath: string, headCommitId: string): Promise<ProjectLocalDiff> {
const relativePath = path.relative(repositoryPath, projectPath); const baseCommit = await getCommit(repositoryPath, headCommitId);
const projectFilePath = path.join(relativePath, 'project.json').replaceAll('\\', '/'); const baseProjectJson = await baseCommit.getFileAsString('project.json');
return projectFilePath;
}
export async function doLocalDiff(
repositoryPath: string,
projectPath: string,
headCommitId: string
): Promise<ProjectLocalDiff> {
const projectFilePath = getProjectFilePath(repositoryPath, projectPath);
try {
const baseCommit = await getCommit(projectPath, headCommitId);
const baseProjectJson = await baseCommit.getFileAsString(projectFilePath);
const baseProject = JSON.parse(baseProjectJson); const baseProject = JSON.parse(baseProjectJson);
applyPatches(baseProject); applyPatches(baseProject);
@@ -113,23 +97,4 @@ export async function doLocalDiff(
baseProject, baseProject,
commitShaDiffedTo: headCommitId 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()
};
}
} }

View File

@@ -8,7 +8,6 @@ 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,
@@ -58,8 +57,7 @@ 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 projectPath = ProjectModel.instance._retainedProjectDirectory; const diff = await doLocalDiff(git.repositoryPath, currentCommitSha);
const diff = await doLocalDiff(git.repositoryPath, projectPath, currentCommitSha);
setLocalDiff(diff); setLocalDiff(diff);
} }
})(); })();

View File

@@ -17,11 +17,9 @@ 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');
@@ -963,7 +961,7 @@ export class ComponentsPanelView extends View {
forRuntimeType: this.getRuntimeType() forRuntimeType: this.getRuntimeType()
}); });
let items: PopupMenuItem[] = templates.map((t) => ({ let items: TSFixme[] = templates.map((t) => ({
icon: IconName.Plus, icon: IconName.Plus,
label: t.label, label: t.label,
onClick: () => { onClick: () => {
@@ -989,10 +987,6 @@ 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,
@@ -1017,9 +1011,6 @@ export class ComponentsPanelView extends View {
_this.onDeleteClicked(scope, el); _this.onDeleteClicked(scope, el);
evt.stopPropagation(); evt.stopPropagation();
} }
},
{
label: nodeReferencesText
} }
]); ]);
@@ -1119,16 +1110,6 @@ 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
}); });

View File

@@ -182,7 +182,7 @@ function AiNodeChat({ context, onUpdated }: AiNodeChatProps) {
footer={ footer={
version === 'disabled' ? ( version === 'disabled' ? (
<Center> <Center>
<Text textType={TextType.Shy}>FluxScape AI is currently disabled.</Text> <Text textType={TextType.Shy}>Noodl AI is currently disabled.</Text>
</Center> </Center>
) : ( ) : (
<> <>

View File

@@ -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, SearchResult } from '@noodl-utils/universal-search'; import { performSearch } 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<ReturnType<typeof performSearch>>([]); const [searchResults, setSearchResults] = useState([]);
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: SearchResult) { function onSearchItemClicked(searchResult) {
if (searchResult.type === 'Component') { if (searchResult.type === 'Component') {
NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, { NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, {
breadcrumbs: false, breadcrumbs: false,
@@ -85,7 +85,29 @@ export function SearchPanel() {
<div className={css.SearchResults}> <div className={css.SearchResults}>
{searchResults.map((component) => ( {searchResults.map((component) => (
<SearchItem key={component.componentId} component={component} onSearchItemClicked={onSearchItemClicked} /> <Section
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>
@@ -96,39 +118,3 @@ 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>
);
}

View File

@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Fluxscape Viewer</title> <title>Noodl Viewer</title>
<link href="../../assets/lib/fontawesome/css/font-awesome.min.css" rel="stylesheet"> <link href="../../assets/lib/fontawesome/css/font-awesome.min.css" rel="stylesheet">
<link href="assets/style.css" rel="stylesheet"> <link href="assets/style.css" rel="stylesheet">
<script type="text/javascript" src="../../assets/lib/jquery-min.js"></script> <script type="text/javascript" src="../../assets/lib/jquery-min.js"></script>

View File

@@ -67,7 +67,7 @@ function startServer(app, projectGetSettings, projectGetInfo, projectGetComponen
ProjectModules.instance.injectIntoHtml(info.projectDirectory, data, '/', function (injected) { ProjectModules.instance.injectIntoHtml(info.projectDirectory, data, '/', function (injected) {
projectGetSettings((settings) => { projectGetSettings((settings) => {
settings = settings || {}; settings = settings || {};
injected = injected.replace('{{#title#}}', settings.htmlTitle || 'Fluxscape Viewer'); injected = injected.replace('{{#title#}}', settings.htmlTitle || 'Noodl Viewer');
injected = injected.replace('{{#customHeadCode#}}', settings.headCode || ''); injected = injected.replace('{{#customHeadCode#}}', settings.headCode || '');
response.writeHead(200, { response.writeHead(200, {

View File

@@ -3,8 +3,8 @@
"version": "2.7.0", "version": "2.7.0",
"main": "src/index.ts", "main": "src/index.ts",
"description": "", "description": "",
"author": "Fluxscape <contact@fluxscape.io>", "author": "Noodl <info@noodl.net>",
"homepage": "https://fluxscape.io", "homepage": "https://noodl.net",
"dependencies": { "dependencies": {
"desktop-trampoline": "https://github.com/desktop/desktop-trampoline/archive/refs/tags/v0.9.8.tar.gz", "desktop-trampoline": "https://github.com/desktop/desktop-trampoline/archive/refs/tags/v0.9.8.tar.gz",
"dugite": "^1.106.0", "dugite": "^1.106.0",

View File

@@ -22,11 +22,7 @@ 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;
} }
/** /**

View File

@@ -3,8 +3,8 @@
"version": "2.7.0", "version": "2.7.0",
"main": "src/index.ts", "main": "src/index.ts",
"description": "Cross platform implementation of platform specific features.", "description": "Cross platform implementation of platform specific features.",
"author": "Fluxscape <contact@fluxscape.io>", "author": "Noodl <info@noodl.net>",
"homepage": "https://fluxscape.io", "homepage": "https://noodl.net",
"dependencies": { "dependencies": {
"@noodl/platform": "file:../noodl-platform", "@noodl/platform": "file:../noodl-platform",
"@noodl/platform-node": "file:../noodl-platform-node" "@noodl/platform-node": "file:../noodl-platform-node"

View File

@@ -3,8 +3,8 @@
"version": "2.7.0", "version": "2.7.0",
"main": "src/index.ts", "main": "src/index.ts",
"description": "Cross platform implementation of platform specific features.", "description": "Cross platform implementation of platform specific features.",
"author": "Fluxscape <contact@fluxscape.io>", "author": "Noodl <info@noodl.net>",
"homepage": "https://fluxscape.io", "homepage": "https://noodl.net",
"scripts": { "scripts": {
"test": "jest", "test": "jest",
"test:coverage": "jest --coverage" "test:coverage": "jest --coverage"

View File

@@ -3,6 +3,6 @@
"version": "2.7.0", "version": "2.7.0",
"main": "src/index.ts", "main": "src/index.ts",
"description": "Cross platform implementation of platform specific features.", "description": "Cross platform implementation of platform specific features.",
"author": "Fluxscape <contact@fluxscape.io>", "author": "Noodl <info@noodl.net>",
"homepage": "https://fluxscape.io" "homepage": "https://noodl.net"
} }

View File

@@ -32,15 +32,12 @@ 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() {
@@ -73,9 +70,8 @@ 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'];
@@ -172,10 +168,13 @@ 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 = {};
@@ -189,20 +188,7 @@ class CloudStore {
grouping[k] = _g; grouping[k] = _g;
}); });
// 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)); 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) {
@@ -258,34 +244,11 @@ 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('&') : ''),
@@ -457,8 +420,6 @@ class CloudStore {
* file: { * file: {
* name: string; * name: string;
* } * }
* success: (data: unknown) => void;
* error: (error: unknown) => void;
* }} options * }} options
*/ */
deleteFile(options) { deleteFile(options) {
@@ -471,15 +432,8 @@ class CloudStore {
} }
function _isArrayOfObjects(a) { function _isArrayOfObjects(a) {
if (!Array.isArray(a)) { if (!Array.isArray(a)) return false;
return false; for (var i = 0; i < a.length; i++) if (typeof a[i] !== 'object' || a[i] === null) return false;
}
for (let i = 0; i < a.length; i++) {
if (typeof a[i] !== 'object' || a[i] === null) {
return false;
}
}
return true; return true;
} }
@@ -551,104 +505,66 @@ 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 undefined; if (data === undefined) return;
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 = [];
if (_isArrayOfObjects(data)) { for (var i = 0; i < data.length; i++) {
const a = [];
for (let i = 0; i < data.length; i++) {
a.push(_deserializeJSON(data[i], undefined, modelScope)); a.push(_deserializeJSON(data[i], undefined, modelScope));
} }
const c = Collection.get(); var 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
if (data && data.__type === 'Object' && data.className !== undefined && data.objectId !== undefined) { else 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) {
m.set(key, _deserializeJSON(data[key], undefined, modelScope));
} }
return m;
if (typeof data === 'object' && data !== null) { } else return data;
// 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 model;
}
return data;
} }
function _fromJSON(item, collectionName, modelScope) { function _fromJSON(item, collectionName, modelScope) {
const modelStore = modelScope || Model; const m = (modelScope || Model).get(item.objectId);
m._class = collectionName;
// Try to get the model by the object id (record) or id, otherwise we create a new unique id. if (collectionName !== undefined && CloudStore._collections[collectionName] !== undefined)
const model = modelStore.get(item.objectId || item.id); var schema = CloudStore._collections[collectionName].schema;
model._class = collectionName;
let schema = undefined; for (var key in item) {
if (collectionName !== undefined && CloudStore._collections[collectionName] !== undefined) { if (key === 'objectId' || key === 'ACL') continue;
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));
} }
for (const key in item) { return m;
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;

View File

@@ -40,7 +40,8 @@ function convertVisualFilter(query, options) {
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;
} }
@@ -79,6 +80,7 @@ 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;
@@ -161,22 +163,10 @@ 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) { if (keys.length !== 1) return options.error('Filter must only have one key found ' + keys.join(','));
return options.error('Filter must only have one key found ' + keys.join(','));
}
const res = {}; const res = {};
const key = keys[0]; const key = keys[0];
@@ -189,27 +179,18 @@ 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) {
const modelId = filter['relatedTo']['id']; var modelId = filter['relatedTo']['id'];
if (modelId === undefined) { if (modelId === undefined) return options.error('Must provide id in relatedTo filter');
return options.error('Must provide id in relatedTo filter');
}
const relationKey = filter['relatedTo']['key']; var relationKey = filter['relatedTo']['key'];
if (relationKey === undefined) { if (relationKey === undefined) return options.error('Must provide key in relatedTo filter');
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 className: m._class
}, },
key: relationKey key: relationKey
}; };
@@ -227,14 +208,13 @@ 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) {
let schema = null; var m = (options.modelScope || Model).get(opAndValue['pointsTo']);
if (CloudStore._collections[options.collectionName]) { if (CloudStore._collections[options.collectionName])
schema = CloudStore._collections[options.collectionName].schema; var schema = CloudStore._collections[options.collectionName].schema;
}
const targetClass = var targetClass =
schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined; schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined;
const type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined; var type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
if (type === 'Relation') { if (type === 'Relation') {
res[key] = { res[key] = {
@@ -243,13 +223,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',
@@ -258,7 +238,6 @@ function convertFilterOp(filter, options) {
} }
}; };
} }
}
} else if (opAndValue['matchesRegex'] !== undefined) { } else if (opAndValue['matchesRegex'] !== undefined) {
res[key] = { res[key] = {
$regex: opAndValue['matchesRegex'], $regex: opAndValue['matchesRegex'],
@@ -283,9 +262,9 @@ function convertFilterOp(filter, options) {
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,
@@ -295,8 +274,8 @@ function convertFilterOp(filter, options) {
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
})) }))
@@ -306,14 +285,15 @@ function convertFilterOp(filter, options) {
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(','));
} }

View File

@@ -12,7 +12,6 @@ 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,
@@ -40,7 +39,6 @@ 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,
@@ -62,7 +60,6 @@ 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,
@@ -85,7 +82,6 @@ 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,
@@ -107,35 +103,19 @@ 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) { if (!className) return reject('No class name specified');
return reject('No class name specified');
}
cloudstore().fetch({ cloudstore().fetch({
collection: className, collection: className,
objectId: objectOrId, objectId: objectOrId,
keys: options?.keys, include: options ? options.include : undefined,
include: options?.include,
excludeKeys: options?.excludeKeys,
success: function (response) { success: function (response) {
const record = cloudstore()._fromJSON(response, className); var record = cloudstore()._fromJSON(response, className);
resolve(record); resolve(record);
}, },
error: function (err) { error: function (err) {
@@ -146,7 +126,6 @@ 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;
@@ -170,7 +149,6 @@ 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;
@@ -201,7 +179,6 @@ 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,
@@ -220,7 +197,6 @@ 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;
@@ -289,7 +265,7 @@ function createRecordsAPI(modelScope) {
resolve(); resolve();
}, },
error: (err) => { error: (err) => {
reject(Error(err || 'Failed to add relation.')); reject(Error(rr || 'Failed to add relation.'));
} }
}); });
}); });

View File

@@ -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 {}
const collections = (Collection._collections = {}); var 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,18 +439,14 @@ 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,
}); });
} }

View File

@@ -35,11 +35,6 @@ 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]) {
@@ -127,22 +122,13 @@ 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
const path = name.split('.'); var path = name.split('.');
let model = this; var model = this;
for (let i = 0; i < path.length - 1; i++) { for (var i = 0; i < path.length - 1; i++) {
const v = model.get(path[i]); var 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
} }
@@ -152,35 +138,24 @@ Model.prototype.set = function (name, value, args) {
const forceChange = args && args.forceChange; const forceChange = args && args.forceChange;
const oldValue = this.data[name]; var oldValue = this.data[name];
this.data[name] = value; this.data[name] = value;
(forceChange || oldValue !== value) &&
if ((forceChange || oldValue !== value) && (!args || !args.silent)) { (!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
const path = name.split('.'); var path = name.split('.');
let model = this; var model = this;
for (let i = 0; i < path.length - 1; i++) { for (var i = 0; i < path.length - 1; i++) {
const v = model.get(path[i]); var 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
} }

View File

@@ -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,15 +455,6 @@ 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;

View File

@@ -28,7 +28,6 @@ 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;
}); });
}; };
@@ -67,7 +66,6 @@ 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') {
@@ -93,7 +91,6 @@ 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);
@@ -109,7 +106,6 @@ var DbCollectionNode = {
_this.flagOutputDirty('count'); _this.flagOutputDirty('count');
_this.flagOutputDirty('firstItemId'); _this.flagOutputDirty('firstItemId');
_this.flagOutputDirty('isEmpty');
} }
} }
}; };
@@ -157,17 +153,6 @@ 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',
@@ -204,7 +189,6 @@ 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');
}, },
@@ -285,7 +269,8 @@ var DbCollectionNode = {
} }
if(count !== undefined) { if(count !== undefined) {
this._internal.storageSettings.storageTotalCount = count; this._internal.storageSettings.storageTotalCount = count;
if (this.hasOutput('storageTotalCount')) this.flagOutputDirty('storageTotalCount'); if(this.hasOutput('storageTotalCount'))
this.flagOutputDirty('storageTotalCount');
} }
this.setCollection(_c); this.setCollection(_c);
this.sendSignalOnOutput('fetched'); this.sendSignalOnOutput('fetched');

View File

@@ -2,10 +2,10 @@
const { Node, EdgeTriggeredInput } = require('../../../../noodl-runtime'); const { Node, EdgeTriggeredInput } = require('../../../../noodl-runtime');
const Model = require('../../../model'); var Model = require('../../../model');
const CloudStore = require('../../../api/cloudstore'); const CloudStore = require('../../../api/cloudstore');
const ModelNodeDefinition = { var 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 @@ const ModelNodeDefinition = {
} }
], ],
initialize: function () { initialize: function () {
const internal = this._internal; var internal = this._internal;
internal.inputValues = {}; internal.inputValues = {};
internal.relationModelIds = {}; internal.relationModelIds = {};
const _this = this; var _this = this;
this._internal.onModelChangedCallback = function (args) { this._internal.onModelChangedCallback = function (args) {
if (_this.isInputConnected('fetch')) return; if (_this.isInputConnected('fetch')) return;
@@ -109,18 +109,13 @@ const ModelNodeDefinition = {
displayName: 'Id', displayName: 'Id',
group: 'General', group: 'General',
set: function (value) { set: function (value) {
if (value instanceof Model) { if (value instanceof Model) value = value.getId();
// Can be passed as model as well // Can be passed as model as well
value = value.getId(); else if (typeof value === 'object') value = Model.create(value).getId(); // If this is an js object, dereference it
} 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) { if (this.isInputConnected('fetch') === false) this.setModelID(value);
this.setModelID(value); else {
} else {
this.flagOutputDirty('id'); this.flagOutputDirty('id');
} }
} }
@@ -143,10 +138,9 @@ const 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');
@@ -154,9 +148,7 @@ const 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)) { if (this.hasOutput('prop-' + key)) this.flagOutputDirty('prop-' + key);
this.flagOutputDirty('prop-' + key);
}
} }
this.sendSignalOnOutput('fetched'); this.sendSignalOnOutput('fetched');
}, },
@@ -192,7 +184,7 @@ const ModelNodeDefinition = {
} }
}, },
scheduleFetch: function () { scheduleFetch: function () {
const _this = this; var _this = this;
const internal = this._internal; const internal = this._internal;
this.scheduleOnce('Fetch', function () { this.scheduleOnce('Fetch', function () {
@@ -207,13 +199,12 @@ const 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) {
const model = cloudstore._fromJSON(response, internal.collectionId); var 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);
@@ -222,10 +213,8 @@ const ModelNodeDefinition = {
delete response.objectId; delete response.objectId;
for (const key in response) { for (var key in response) {
if (_this.hasOutput('prop-' + key)) { if (_this.hasOutput('prop-' + key)) _this.flagOutputDirty('prop-' + key);
_this.flagOutputDirty('prop-' + key);
}
} }
_this.sendSignalOnOutput('fetched'); _this.sendSignalOnOutput('fetched');
@@ -237,6 +226,7 @@ const ModelNodeDefinition = {
}); });
}, },
scheduleStore: function () { scheduleStore: function () {
var _this = this;
var internal = this._internal; var internal = this._internal;
if (!internal.model) return; if (!internal.model) return;
@@ -257,6 +247,8 @@ const ModelNodeDefinition = {
}); });
}, },
registerInputIfNeeded: function (name) { registerInputIfNeeded: function (name) {
var _this = this;
if (this.hasInput(name)) { if (this.hasInput(name)) {
return; return;
} }
@@ -336,7 +328,8 @@ 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',
@@ -380,16 +373,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 () { node.on('parameterUpdated', function (event) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel); updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
}); });
graphModel.on('metadataChanged.dbCollections', function () { graphModel.on('metadataChanged.dbCollections', function (data) {
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 () { graphModel.on('metadataChanged.systemCollections', function (data) {
CloudStore.invalidateCollections(); CloudStore.invalidateCollections();
updatePorts(node.id, node.parameters, context.editorConnection, graphModel); updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
}); });

View File

@@ -75,10 +75,10 @@ var SetDbModelPropertiedNodeDefinition = {
_this.setError('Missing Record Id'); _this.setError('Missing Record Id');
return; return;
} }
var model = internal.model;
const model = internal.model; for (var i in internal.inputValues) {
for (const key in internal.inputValues) { model.set(i, internal.inputValues[i], { resolve: true });
model.set(key, internal.inputValues[key], { resolve: true });
} }
CloudStore.forScope(_this.nodeScope.modelScope).save({ CloudStore.forScope(_this.nodeScope.modelScope).save({

View File

@@ -31,6 +31,8 @@ const DateToStringNode = {
this._internal.currentInput = _value; this._internal.currentInput = _value;
this._format(); this._format();
this.flagOutputDirty('currentValue');
this.sendSignalOnOutput('inputChanged');
} }
} }
}, },
@@ -47,23 +49,16 @@ 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() {
try {
const t = this._internal.currentInput; const t = this._internal.currentInput;
const format = this._internal.formatString; const format = this._internal.formatString;
const date = ('0' + t.getDate()).slice(-2); const date = ('0' + t.getDate()).slice(-2);
const month = ('0' + (t.getMonth() + 1)).slice(-2); const month = ('0' + (t.getMonth() + 1)).slice(-2);
const monthShort = new Intl.DateTimeFormat('en-US', { month: 'short' }).format(t); const monthShort = new Intl.DateTimeFormat('en-US', { month: 'short' }).format(t);
const year = t.getFullYear(); const year = t.getFullYear();
const yearShort = year.toString().substring(2);
const hours = ('0' + t.getHours()).slice(-2); const hours = ('0' + t.getHours()).slice(-2);
const minutes = ('0' + t.getMinutes()).slice(-2); const minutes = ('0' + t.getMinutes()).slice(-2);
const seconds = ('0' + t.getSeconds()).slice(-2); const seconds = ('0' + t.getSeconds()).slice(-2);
@@ -73,19 +68,9 @@ const DateToStringNode = {
.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(/\{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');
} }
} }
}; };

View File

@@ -2,8 +2,6 @@ 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 = '';

View File

@@ -3,6 +3,6 @@
"version": "2.7.0", "version": "2.7.0",
"main": "src/index.d.ts", "main": "src/index.d.ts",
"description": "", "description": "",
"author": "Fluxscape <contact@fluxscape.io>", "author": "Noodl <info@noodl.net>",
"homepage": "https://fluxscape.io" "homepage": "https://noodl.net"
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@noodl/cloud-runtime", "name": "@noodl/cloud-runtime",
"author": "Fluxscape <contact@fluxscape.io>", "author": "Noodl <info@noodl.net>",
"homepage": "https://fluxscape.io", "homepage": "https://noodl.net",
"version": "0.6.3", "version": "0.6.3",
"license": "MIT", "license": "MIT",
"main": "dist/main.js", "main": "dist/main.js",

View File

@@ -74,59 +74,6 @@ declare namespace Noodl {
*/ */
const Object: any; const Object: any;
type RecordQuery<T> =
{
lessThan: T
} |
{
lessThanOrEqualTo: T
} |
{
greaterThan: T
} |
{
greaterThanOrEqualTo: T
} |
{
equalTo: T
} |
{
notEqualTo: T
} |
{
containedIn: T
} |
{
notContainedIn : T
} |
{
exists: T
} |
{
matchesRegex: T
} |
{
text: T
} |
{
idEqualTo: T
} |
{
idContainedIn: T
} |
{
pointsTo: T
} |
{
relatedTo: T
};
type RecordQueryField<T> = T extends RecordQuery<any> ?
{ [K in keyof T]: { [P in K]: T[P] } & Partial<Record<Exclude<keyof T, K>, never>> }[keyof T]
: never;
type RecordSortKey<T extends string> = (`${T}` | `-${T}`)[];
interface RecordsApi { interface RecordsApi {
/** /**
* This is an async function that will query the database using the query * This is an async function that will query the database using the query
@@ -168,17 +115,15 @@ declare namespace Noodl {
* }) * })
* ``` * ```
*/ */
query<TClassName extends RecordClassName>( query(
className: TClassName, className: RecordClassName,
query?: query?: any,
RecordQueryField<{ [K in keyof DatabaseSchema[TClassName]]: RecordQuery<any> }> |
{ and: RecordQueryField<{ [K in keyof DatabaseSchema[TClassName]]: RecordQuery<any> }>[] },
options?: { options?: {
limit?: number; limit?: number;
skip?: number; skip?: number;
sort?: string | RecordSortKey<keyof DatabaseSchema[TClassName]>; sort?: string[];
include?: string | (keyof DatabaseSchema[TClassName])[]; include?: any;
select?: string | (keyof DatabaseSchema[TClassName])[]; select?: any;
} }
): Promise<any>; ): Promise<any>;
@@ -227,9 +172,6 @@ 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>;

View File

@@ -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,
}); });

View File

@@ -2,6 +2,5 @@ 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'
}); });

View File

@@ -2,18 +2,12 @@ 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
}); });
} }

View File

@@ -8,8 +8,6 @@ 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;
@@ -98,7 +96,6 @@ 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)}

View File

@@ -9,8 +9,6 @@ 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;
@@ -49,7 +47,6 @@ 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,

View File

@@ -10,8 +10,6 @@ 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;
@@ -49,7 +47,6 @@ 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(' '),

View File

@@ -12,8 +12,6 @@ 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;
@@ -83,7 +81,6 @@ 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: {

View File

@@ -2,15 +2,13 @@ import React, { useEffect, useState } from 'react';
import Layout from '../../../layout'; import Layout from '../../../layout';
import Utils from '../../../nodes/controls/utils'; import Utils from '../../../nodes/controls/utils';
import { Noodl } from '../../../types'; import { Noodl, Slot } from '../../../types';
export interface SliderProps extends Noodl.ReactProps { export interface SliderProps extends Noodl.ReactProps {
_nodeId: string; _nodeId: string;
id: string; id: string;
enabled: boolean; enabled: boolean;
attrs: React.Attributes;
value: number; value: number;
min: number; min: number;
max: number; max: number;
@@ -26,6 +24,8 @@ export interface SliderProps extends Noodl.ReactProps {
onClick: () => void; onClick: () => void;
updateOutputValue: (value: number) => void; updateOutputValue: (value: number) => void;
children: Slot;
} }
function _styleTemplate(_class: string, props: SliderProps) { function _styleTemplate(_class: string, props: SliderProps) {
@@ -105,7 +105,6 @@ 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%',
@@ -171,7 +170,9 @@ export function Slider(props: SliderProps) {
return ( return (
<div style={divStyle}> <div style={divStyle}>
<div style={trackStyle} /> <div style={trackStyle} />
<div style={thumbStyle} /> <div style={thumbStyle}>
{props.children}
</div>
<input <input
className={className} className={className}
{...Utils.controlEvents(props)} {...Utils.controlEvents(props)}

View File

@@ -17,8 +17,6 @@ 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;
@@ -151,7 +149,6 @@ 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),

View File

@@ -7,8 +7,6 @@ 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;
@@ -117,10 +115,7 @@ 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)) {
// @ts-expect-error props.children.type is any children = [props.children];
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);
@@ -128,11 +123,9 @@ 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',

View File

@@ -19,8 +19,6 @@ 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;
@@ -273,7 +271,6 @@ 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}

View File

@@ -10,7 +10,6 @@ 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) {
@@ -31,5 +30,5 @@ export function Image(props: ImageProps) {
} }
} }
return <img {...props.attrs} className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />; return <img className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />;
} }

View File

@@ -7,8 +7,6 @@ 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;
@@ -50,7 +48,6 @@ 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}

View File

@@ -1,9 +1,10 @@
'use strict'; 'use strict';
const { Node } = require('@noodl/runtime'); const { Node } = require('@noodl/runtime');
const Model = require('@noodl/runtime/src/model');
const VariableNodeDefinition = { var Model = require('@noodl/runtime/src/model');
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',

View File

@@ -30,24 +30,11 @@ 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,

View File

@@ -31,7 +31,6 @@ 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;
@@ -95,16 +94,6 @@ 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: {

View File

@@ -31,7 +31,6 @@ const OptionsNode = {
] ]
}, },
initialize: function () { initialize: function () {
this.props.attrs = {};
this._itemsChanged = () => { this._itemsChanged = () => {
this.forceUpdate(); this.forceUpdate();
}; };
@@ -91,16 +90,6 @@ 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: {

View File

@@ -31,7 +31,6 @@ 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();
@@ -62,16 +61,6 @@ 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: {

View File

@@ -11,7 +11,6 @@ const RangeNode = {
name: 'net.noodl.controls.range', name: 'net.noodl.controls.range',
displayNodeName: 'Slider', displayNodeName: 'Slider',
docs: 'https://docs.noodl.net/nodes/ui-controls/slider', docs: 'https://docs.noodl.net/nodes/ui-controls/slider',
allowChildren: false,
noodlNodeAsProp: true, noodlNodeAsProp: true,
connectionPanel: { connectionPanel: {
groupPriority: [ groupPriority: [
@@ -27,7 +26,6 @@ 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();
@@ -68,16 +66,6 @@ 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: {

Some files were not shown because too many files have changed in this diff Show More