Compare commits
24 Commits
feature/sl
...
fix/base-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0668d0e928 | ||
|
|
d61effc615 | ||
|
|
12be6dc69f | ||
|
|
17e3d16436 | ||
|
|
233479a1bc | ||
|
|
57e5246022 | ||
|
|
dda22e0de6 | ||
|
|
618955e1ee | ||
|
|
8c7d4faeca | ||
|
|
c5754c9160 | ||
|
|
3fb3668fc3 | ||
|
|
fa282d6169 | ||
|
|
0ee55c26eb | ||
|
|
3a31b86d48 | ||
|
|
2f06952e4a | ||
|
|
44a40aef96 | ||
|
|
0a69765460 | ||
|
|
2ebd57b29a | ||
|
|
5225d26870 | ||
|
|
eb71536cb0 | ||
|
|
d67afd3c60 | ||
|
|
1a048587d9 | ||
|
|
8e16d5f9d4 | ||
|
|
cdeb4b1c15 |
32
README.md
@@ -1,21 +1,21 @@
|
|||||||
# Noodl
|
# Fluxscape
|
||||||
|
|
||||||
[Noodl](https://noodl.net) is a low-code platform where designers and developers build custom applications and experiences. Designed as a visual programming environment, it aims to expedite your development process. It promotes the swift and efficient creation of applications, requiring minimal coding knowledge.
|
Fluxscape is a low-code platform where designers and developers build custom applications and experiences. Designed as a visual programming environment, it aims to expedite your development process. It promotes the swift and efficient creation of applications, requiring minimal coding knowledge.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
Documentation for how to use Noodl can be found here:
|
Documentation for how to use Fluxscape can be found here:
|
||||||
[https://noodlapp.github.io/noodl-docs/](https://noodlapp.github.io/noodl-docs/)
|
[Fluxscape Documentation](https://docs.fluxscape.io)
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
Main support channel is Discord: [https://www.noodl.net/community](https://www.noodl.net/community)
|
Main support channel is Discord: [Fluxscape Discord](https://discord.gg/fXNW9EXa6A)
|
||||||
|
|
||||||
## Download releases
|
## Download releases
|
||||||
Pre-built binaries can be [downloaded from Github](https://github.com/noodlapp/noodl/releases)
|
Pre-built binaries can be [downloaded from Github](https://github.com/fluxscape/fluxscape/releases)
|
||||||
|
|
||||||
## Note for users who are migrating from the deprecated closed source version
|
## Note for users who are migrating from the deprecated closed source version
|
||||||
- [Migrating the project files and workspaces to a Git provider](https://noodlapp.github.io/noodl-docs/docs/guides/collaboration/migrating-from-noodl-hosted-git)
|
- [Migrating the project files and workspaces to a Git provider](https://docs.fluxscape.io/docs/guides/collaboration/migrating-from-noodl-hosted-git/)
|
||||||
- [Migrate backend and database](https://noodlapp.github.io/noodl-docs/docs/guides/deploy/using-an-external-backend#migrating-from-a-noodl-cloud-service)
|
- [Migrate backend and database](https://docs.fluxscape.io/docs/guides/deploy/using-an-external-backend/#migrating-from-a-noodl-cloud-service)
|
||||||
- [Self-host frontend](https://noodlapp.github.io/noodl-docs/docs/guides/deploy/hosting-frontend)
|
- [Self-host frontend](https://docs.fluxscape.io/docs/guides/deploy/hosting-frontend/)
|
||||||
|
|
||||||
## Building from source
|
## Building from source
|
||||||
|
|
||||||
@@ -23,24 +23,24 @@ Pre-built binaries can be [downloaded from Github](https://github.com/noodlapp/n
|
|||||||
# Install all dependencies
|
# Install all dependencies
|
||||||
$ npm install
|
$ npm install
|
||||||
|
|
||||||
# Start the Noodl Editor and build a production version of the cloud and react runtime (useful when running Noodl from source but want to deploy to production)
|
# Start the Fluxscape Editor and build a production version of the cloud and react runtime (useful when running Fluxscape from source but want to deploy to production)
|
||||||
$ npm start
|
$ npm start
|
||||||
|
|
||||||
# Start the Noodl Editor and watch the filesystem for changes to the runtimes. Development versions of the runtimes, not meant for production (mostly due to source maps and file size)
|
# Start the Fluxscape Editor and watch the filesystem for changes to the runtimes. Development versions of the runtimes, not meant for production (mostly due to source maps and file size)
|
||||||
# This is ideal for a quick workflow when doing changes on the runtimes.
|
# This is ideal for a quick workflow when doing changes on the runtimes.
|
||||||
$ npm run dev
|
$ npm run dev
|
||||||
|
|
||||||
# Start Noodl Editor test runner
|
# Start Fluxscape Editor test runner
|
||||||
$ npm run test:editor
|
$ npm run test:editor
|
||||||
```
|
```
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
This repository contains two different licenses for different parts of the Noodl platform.
|
This repository contains two different licenses for different parts of the Fluxscape platform.
|
||||||
|
|
||||||
- Components related to the editor, used to edit Noodl projects, are under GPLv3
|
- Components related to the editor, used to edit Fluxscape projects, are under GPLv3
|
||||||
- Components related to the end applications, used by the applications Noodl deploys, are under MIT
|
- Components related to the end applications, used by the applications Fluxscape deploys, are under MIT
|
||||||
|
|
||||||
All of the source code of applications created with Noodl are under MIT. This means you can do project specific changes to the runtime without having to redistribute your changes.
|
All of the source code of applications created with Fluxscape are under MIT. This means you can do project specific changes to the runtime without having to redistribute your changes.
|
||||||
|
|
||||||
Packaged licensed under MIT:
|
Packaged licensed under MIT:
|
||||||
- `noodl-runtime`
|
- `noodl-runtime`
|
||||||
|
|||||||
64
package-lock.json
generated
@@ -2849,9 +2849,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron/remote": {
|
"node_modules/@electron/remote": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz",
|
||||||
"integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
|
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"electron": ">= 13.0.0"
|
"electron": ">= 13.0.0"
|
||||||
}
|
}
|
||||||
@@ -25220,13 +25220,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron": {
|
"node_modules/electron": {
|
||||||
"version": "28.1.4",
|
"version": "31.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-31.1.0.tgz",
|
||||||
"integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
|
"integrity": "sha512-TBOwqLxSxnx6+pH6GMri7R3JPH2AkuGJHfWZS0p1HsmN+Qr1T9b0IRJnnehSd/3NZAmAre4ft9Ljec7zjyKFJA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/get": "^2.0.0",
|
"@electron/get": "^2.0.0",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^20.9.0",
|
||||||
"extract-zip": "^2.0.1"
|
"extract-zip": "^2.0.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -25452,6 +25452,14 @@
|
|||||||
"tiny-typed-emitter": "^2.1.0"
|
"tiny-typed-emitter": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/electron/node_modules/@types/node": {
|
||||||
|
"version": "20.14.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
|
||||||
|
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/elliptic": {
|
"node_modules/elliptic": {
|
||||||
"version": "6.5.4",
|
"version": "6.5.4",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -43191,6 +43199,11 @@
|
|||||||
"version": "1.13.6",
|
"version": "1.13.6",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
|
},
|
||||||
"node_modules/unfetch": {
|
"node_modules/unfetch": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -48969,7 +48982,7 @@
|
|||||||
"packages/noodl-editor": {
|
"packages/noodl-editor": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^2.1.1",
|
"@electron/remote": "^2.1.2",
|
||||||
"@jaames/iro": "^5.5.2",
|
"@jaames/iro": "^5.5.2",
|
||||||
"@microsoft/fetch-event-source": "^2.0.1",
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
"@noodl/git": "file:../noodl-git",
|
"@noodl/git": "file:../noodl-git",
|
||||||
@@ -49025,7 +49038,7 @@
|
|||||||
"babel-loader": "^8.2.4",
|
"babel-loader": "^8.2.4",
|
||||||
"concurrently": "^7.4.0",
|
"concurrently": "^7.4.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"electron": "28.1.4",
|
"electron": "^31.1.0",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.9.1",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-loader": "^3.1.0",
|
"html-loader": "^3.1.0",
|
||||||
@@ -53375,9 +53388,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@electron/remote": {
|
"@electron/remote": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz",
|
||||||
"integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
|
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"@electron/universal": {
|
"@electron/universal": {
|
||||||
@@ -74057,13 +74070,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "28.1.4",
|
"version": "31.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-31.1.0.tgz",
|
||||||
"integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
|
"integrity": "sha512-TBOwqLxSxnx6+pH6GMri7R3JPH2AkuGJHfWZS0p1HsmN+Qr1T9b0IRJnnehSd/3NZAmAre4ft9Ljec7zjyKFJA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^2.0.0",
|
"@electron/get": "^2.0.0",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^20.9.0",
|
||||||
"extract-zip": "^2.0.1"
|
"extract-zip": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": {
|
||||||
|
"version": "20.14.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
|
||||||
|
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
|
||||||
|
"requires": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron-builder": {
|
"electron-builder": {
|
||||||
@@ -80988,7 +81011,7 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"@babel/core": "^7.19.1",
|
"@babel/core": "^7.19.1",
|
||||||
"@babel/preset-react": "^7.18.6",
|
"@babel/preset-react": "^7.18.6",
|
||||||
"@electron/remote": "^2.1.1",
|
"@electron/remote": "^2.1.2",
|
||||||
"@jaames/iro": "^5.5.2",
|
"@jaames/iro": "^5.5.2",
|
||||||
"@microsoft/fetch-event-source": "^2.0.1",
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
"@noodl/git": "file:../noodl-git",
|
"@noodl/git": "file:../noodl-git",
|
||||||
@@ -81017,7 +81040,7 @@
|
|||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"diff3": "0.0.4",
|
"diff3": "0.0.4",
|
||||||
"dmg-license": "^1.0.11",
|
"dmg-license": "^1.0.11",
|
||||||
"electron": "28.1.4",
|
"electron": "^31.1.0",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.9.1",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"electron-updater": "^6.1.7",
|
"electron-updater": "^6.1.7",
|
||||||
@@ -86403,6 +86426,11 @@
|
|||||||
"underscore": {
|
"underscore": {
|
||||||
"version": "1.13.6"
|
"version": "1.13.6"
|
||||||
},
|
},
|
||||||
|
"undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
|
},
|
||||||
"unfetch": {
|
"unfetch": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
|||||||
@@ -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": "Noodl <info@noodl.net>",
|
"author": "Fluxscape <contact@fluxcsape.io>",
|
||||||
"homepage": "https://noodl.net",
|
"homepage": "https://fluxscape.io",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|||||||
@@ -1,123 +1,128 @@
|
|||||||
.Root {
|
.Root {
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
&.has-backdrop {
|
&.is-hidden {
|
||||||
background-color: var(--theme-color-bg-1-transparent);
|
visibility: hidden;
|
||||||
}
|
pointer-events: none;
|
||||||
|
}
|
||||||
&.is-locking-scroll {
|
|
||||||
background-color: transparent;
|
&.has-backdrop {
|
||||||
}
|
background-color: var(--theme-color-bg-1-transparent);
|
||||||
|
}
|
||||||
&:not(.has-backdrop):not(.is-locking-scroll) {
|
|
||||||
pointer-events: none;
|
&.is-locking-scroll {
|
||||||
}
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VisibleDialog {
|
&:not(.has-backdrop):not(.is-locking-scroll) {
|
||||||
filter: drop-shadow(0 4px 15px var(--theme-color-bg-1-transparent-2));
|
pointer-events: none;
|
||||||
box-shadow: 0 0 10px -5px var(--theme-color-bg-1-transparent-2);
|
}
|
||||||
position: absolute;
|
}
|
||||||
width: var(--width);
|
|
||||||
pointer-events: all;
|
.VisibleDialog {
|
||||||
|
filter: drop-shadow(0 4px 15px var(--theme-color-bg-1-transparent-2));
|
||||||
.Root.is-centered & {
|
box-shadow: 0 0 10px -5px var(--theme-color-bg-1-transparent-2);
|
||||||
top: 50%;
|
position: absolute;
|
||||||
left: 50%;
|
width: var(--width);
|
||||||
animation: enter-centered var(--speed-quick) var(--easing-base) both;
|
pointer-events: all;
|
||||||
}
|
|
||||||
|
.Root.is-centered & {
|
||||||
.Root:not(.is-centered) &.is-visible {
|
top: 50%;
|
||||||
&.is-variant-default {
|
left: 50%;
|
||||||
animation: enter var(--speed-quick) var(--easing-base) both;
|
animation: enter-centered var(--speed-quick) var(--easing-base) both;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-variant-select {
|
.Root:not(.is-centered) &.is-visible {
|
||||||
transform: translate(var(--offsetX), var(--offsetY));
|
&.is-variant-default {
|
||||||
}
|
animation: enter var(--speed-quick) var(--easing-base) both;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&.is-variant-select {
|
||||||
content: '';
|
transform: translate(var(--offsetX), var(--offsetY));
|
||||||
position: absolute;
|
}
|
||||||
top: 0;
|
}
|
||||||
left: 0;
|
|
||||||
right: 0;
|
&::after {
|
||||||
bottom: 0;
|
content: '';
|
||||||
background-color: var(--background);
|
position: absolute;
|
||||||
border-radius: 2px;
|
top: 0;
|
||||||
overflow: hidden;
|
left: 0;
|
||||||
}
|
right: 0;
|
||||||
}
|
bottom: 0;
|
||||||
|
background-color: var(--background);
|
||||||
.Arrow {
|
border-radius: 2px;
|
||||||
position: absolute;
|
overflow: hidden;
|
||||||
width: 0;
|
}
|
||||||
height: 0;
|
}
|
||||||
top: var(--arrow-top);
|
|
||||||
left: var(--arrow-left);
|
.Arrow {
|
||||||
pointer-events: none;
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
&::after {
|
height: 0;
|
||||||
content: '';
|
top: var(--arrow-top);
|
||||||
display: block;
|
left: var(--arrow-left);
|
||||||
width: 11px;
|
pointer-events: none;
|
||||||
height: 11px;
|
|
||||||
transform: translate(-50%, -50%) rotate(45deg);
|
&::after {
|
||||||
background: var(--background);
|
content: '';
|
||||||
}
|
display: block;
|
||||||
|
width: 11px;
|
||||||
&.is-contrast::after {
|
height: 11px;
|
||||||
background: var(--backgroundContrast);
|
transform: translate(-50%, -50%) rotate(45deg);
|
||||||
}
|
background: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.Title {
|
&.is-contrast::after {
|
||||||
background-color: var(--backgroundContrast);
|
background: var(--backgroundContrast);
|
||||||
padding: 12px;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.MeasuringContainer {
|
.Title {
|
||||||
pointer-events: none;
|
background-color: var(--backgroundContrast);
|
||||||
height: 0;
|
padding: 12px;
|
||||||
overflow: visible;
|
}
|
||||||
opacity: 0;
|
|
||||||
}
|
.MeasuringContainer {
|
||||||
|
pointer-events: none;
|
||||||
.ChildContainer {
|
height: 0;
|
||||||
position: relative;
|
overflow: visible;
|
||||||
z-index: 1;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes enter {
|
.ChildContainer {
|
||||||
from {
|
position: relative;
|
||||||
opacity: 0;
|
z-index: 1;
|
||||||
transform: translate(
|
}
|
||||||
calc(var(--animationStartOffsetX) + var(--offsetX)),
|
|
||||||
calc(var(--animationStartOffsetY) + var(--offsetY))
|
@keyframes enter {
|
||||||
);
|
from {
|
||||||
}
|
opacity: 0;
|
||||||
to {
|
transform: translate(
|
||||||
opacity: 1;
|
calc(var(--animationStartOffsetX) + var(--offsetX)),
|
||||||
transform: translate(var(--offsetX), var(--offsetY));
|
calc(var(--animationStartOffsetY) + var(--offsetY))
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
to {
|
||||||
@keyframes enter-centered {
|
opacity: 1;
|
||||||
from {
|
transform: translate(var(--offsetX), var(--offsetY));
|
||||||
opacity: 0;
|
}
|
||||||
transform: translate(-50%, calc(-50% + 16px));
|
}
|
||||||
}
|
|
||||||
to {
|
@keyframes enter-centered {
|
||||||
opacity: 1;
|
from {
|
||||||
transform: translate(-50%, -50%);
|
opacity: 0;
|
||||||
}
|
transform: translate(-50%, calc(-50% + 16px));
|
||||||
}
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export interface BaseDialogProps extends UnsafeStyleProps {
|
|||||||
isVisible?: boolean;
|
isVisible?: boolean;
|
||||||
hasBackdrop?: boolean;
|
hasBackdrop?: boolean;
|
||||||
hasArrow?: boolean;
|
hasArrow?: boolean;
|
||||||
|
alwaysMounted?: boolean;
|
||||||
|
|
||||||
children?: Slot;
|
children?: Slot;
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ export function CoreBaseDialog({
|
|||||||
isVisible,
|
isVisible,
|
||||||
hasBackdrop,
|
hasBackdrop,
|
||||||
hasArrow,
|
hasArrow,
|
||||||
|
alwaysMounted,
|
||||||
|
|
||||||
children,
|
children,
|
||||||
|
|
||||||
@@ -261,7 +263,7 @@ export function CoreBaseDialog({
|
|||||||
}
|
}
|
||||||
}, [background]);
|
}, [background]);
|
||||||
|
|
||||||
if (!isVisible) return null;
|
if (!isVisible && !alwaysMounted) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -270,6 +272,7 @@ export function CoreBaseDialog({
|
|||||||
hasBackdrop && css['has-backdrop'],
|
hasBackdrop && css['has-backdrop'],
|
||||||
isLockingScroll && css['is-locking-scroll'],
|
isLockingScroll && css['is-locking-scroll'],
|
||||||
typeof triggerRef === 'undefined' && css['is-centered'],
|
typeof triggerRef === 'undefined' && css['is-centered'],
|
||||||
|
!isVisible && css['is-hidden'],
|
||||||
css[variant]
|
css[variant]
|
||||||
)}
|
)}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
|||||||
@@ -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;
|
fineType?: string | string[];
|
||||||
children: Slot;
|
children: Slot;
|
||||||
showAfterMs?: number;
|
showAfterMs?: number;
|
||||||
|
|
||||||
@@ -79,9 +79,17 @@ export function Tooltip({
|
|||||||
|
|
||||||
{fineType && (
|
{fineType && (
|
||||||
<div className={css['FineType']}>
|
<div className={css['FineType']}>
|
||||||
<Label size={LabelSize.Small} variant={TextType.Secondary}>
|
{Array.isArray(fineType) ? (
|
||||||
{fineType}
|
fineType.map((x) => (
|
||||||
</Label>
|
<Label size={LabelSize.Small} variant={TextType.Secondary}>
|
||||||
|
{x}
|
||||||
|
</Label>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Label size={LabelSize.Small} variant={TextType.Secondary}>
|
||||||
|
{fineType}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^2.1.1",
|
"@electron/remote": "^2.1.2",
|
||||||
"@jaames/iro": "^5.5.2",
|
"@jaames/iro": "^5.5.2",
|
||||||
"@microsoft/fetch-event-source": "^2.0.1",
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
"@noodl/git": "file:../noodl-git",
|
"@noodl/git": "file:../noodl-git",
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
"babel-loader": "^8.2.4",
|
"babel-loader": "^8.2.4",
|
||||||
"concurrently": "^7.4.0",
|
"concurrently": "^7.4.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"electron": "28.1.4",
|
"electron": "^31.1.0",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.9.1",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-loader": "^3.1.0",
|
"html-loader": "^3.1.0",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3zm-9-3.82l-2.09-2.09L6.5 13.5 10 17l6.01-6.01-1.41-1.41z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 526 B |
|
Before Width: | Height: | Size: 540 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 175 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0V0z"/><path opacity=".87" d="M0 0h24v24H0V0z"/></g><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3zM23 3.01H1V9h2V4.99h18v14.03H3V15H1v5.99h22V3.01zM11 16l4-4-4-4v3H1v2h10v3z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 408 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M4 6H2v16h16v-2H4V6zm18-4H6v16h16V2zm-3 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 220 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0V0z"/><path opacity=".87" d="M0 0h24v24H0V0z"/></g><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7zm-4 6h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 360 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19.44 12.99l-.01.02c.04-.33.08-.67.08-1.01 0-.34-.03-.66-.07-.99l.01.02 2.44-1.92-2.43-4.22-2.87 1.16.01.01c-.52-.4-1.09-.74-1.71-1h.01L14.44 2H9.57l-.44 3.07h.01c-.62.26-1.19.6-1.71 1l.01-.01-2.88-1.17-2.44 4.22 2.44 1.92.01-.02c-.04.33-.07.65-.07.99 0 .34.03.68.08 1.01l-.01-.02-2.1 1.65-.33.26 2.43 4.2 2.88-1.15-.02-.04c.53.41 1.1.75 1.73 1.01h-.03L9.58 22h4.85s.03-.18.06-.42l.38-2.65h-.01c.62-.26 1.2-.6 1.73-1.01l-.02.04 2.88 1.15 2.43-4.2s-.14-.12-.33-.26l-2.11-1.66zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 701 B |
|
Before Width: | Height: | Size: 522 KiB |
|
Before Width: | Height: | Size: 522 KiB |
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Noodl</title>
|
<title>Fluxscape</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" />
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ 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);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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 { Keybinding } from '@noodl-utils/keyboard/Keybinding';
|
import { Keybindings } from '@noodl-constants/Keybindings';
|
||||||
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';
|
||||||
|
|
||||||
@@ -69,7 +68,7 @@ export function installSidePanel({ isLesson }: SetupEditorOptions) {
|
|||||||
SidebarModel.instance.register({
|
SidebarModel.instance.register({
|
||||||
id: 'search',
|
id: 'search',
|
||||||
name: 'Search',
|
name: 'Search',
|
||||||
fineType: new Keybinding(KeyMod.CtrlCmd, KeyCode.KEY_F).label,
|
fineType: Keybindings.SEARCH.label,
|
||||||
order: 2,
|
order: 2,
|
||||||
icon: IconName.Search,
|
icon: IconName.Search,
|
||||||
panel: SearchPanel
|
panel: SearchPanel
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { projectFromDirectory, unzipIntoDirectory } from '../models/projectmodel
|
|||||||
import FileSystem from './filesystem';
|
import FileSystem from './filesystem';
|
||||||
import { tracker } from './tracker';
|
import { tracker } from './tracker';
|
||||||
import { guid } from './utils';
|
import { guid } from './utils';
|
||||||
|
import { getTopLevelWorkingDirectory } from '@noodl/git/src/core/open';
|
||||||
|
|
||||||
export interface ProjectItem {
|
export interface ProjectItem {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -267,12 +268,15 @@ export class LocalProjectsModel extends Model {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isGitProject(project: ProjectModel): boolean {
|
/**
|
||||||
// TODO: check if there's is git in any parent folder too
|
* Check if this project is in a git repository.
|
||||||
|
*
|
||||||
// Check if the git folder exists.
|
* @param project
|
||||||
const gitPath = filesystem.join(project._retainedProjectDirectory, '.git');
|
* @returns
|
||||||
return filesystem.exists(gitPath);
|
*/
|
||||||
|
async isGitProject(project: ProjectModel): Promise<boolean> {
|
||||||
|
const gitPath = await getTopLevelWorkingDirectory(project._retainedProjectDirectory);
|
||||||
|
return gitPath !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentGlobalGitAuth(projectId: string) {
|
setCurrentGlobalGitAuth(projectId: string) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export class HtmlProcessor {
|
|||||||
baseUrl = baseUrl + '/';
|
baseUrl = baseUrl + '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = parameters.title || settings.htmlTitle || 'Noodl Viewer';
|
const title = parameters.title || settings.htmlTitle || 'Fluxscape Viewer';
|
||||||
let headCode = settings.headCode || '';
|
let headCode = settings.headCode || '';
|
||||||
|
|
||||||
if (parameters.headCode) {
|
if (parameters.headCode) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ITemplateProvider, ProgressCallback, TemplateItem, TemplateListFilter }
|
|||||||
*/
|
*/
|
||||||
export class NoodlDocsTemplateProvider implements ITemplateProvider {
|
export class NoodlDocsTemplateProvider implements ITemplateProvider {
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return 'https://docs.noodl.net';
|
return this.getDocsEndpoint() || 'https://docs.fluxscape.io';
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private readonly getDocsEndpoint: () => string) {}
|
constructor(private readonly getDocsEndpoint: () => string) {}
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ export interface ArrayDiff<T> {
|
|||||||
changed: T[];
|
changed: T[];
|
||||||
unchanged: T[];
|
unchanged: T[];
|
||||||
}
|
}
|
||||||
|
export function createEmptyArrayDiff<T>(): ArrayDiff<T> {
|
||||||
|
return {
|
||||||
|
deleted: [],
|
||||||
|
created: [],
|
||||||
|
changed: [],
|
||||||
|
unchanged: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProjectDiffItem {
|
export interface ProjectDiffItem {
|
||||||
graph: TSFixme;
|
graph: TSFixme;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default class SchemaHandler {
|
|||||||
public dbCollections: TSFixme[];
|
public dbCollections: TSFixme[];
|
||||||
public systemCollections: TSFixme[];
|
public systemCollections: TSFixme[];
|
||||||
public configSchema: TSFixme;
|
public configSchema: TSFixme;
|
||||||
|
public parseServerVersion: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
EventDispatcher.instance.on(
|
EventDispatcher.instance.on(
|
||||||
@@ -119,6 +120,20 @@ export default class SchemaHandler {
|
|||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get Parse Server Version & Supported features
|
||||||
|
fetch(environment.url + '/serverInfo', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
"_method": "GET",
|
||||||
|
"_ApplicationId": environment.appId,
|
||||||
|
"_MasterKey": environment.masterKey,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((json) => {
|
||||||
|
this.parseServerVersion = json.parseServerVersion;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -129,10 +144,20 @@ export default class SchemaHandler {
|
|||||||
ProjectModel.instance.setMetaData('dbCollections', this.dbCollections);
|
ProjectModel.instance.setMetaData('dbCollections', this.dbCollections);
|
||||||
ProjectModel.instance.setMetaData('systemCollections', this.systemCollections);
|
ProjectModel.instance.setMetaData('systemCollections', this.systemCollections);
|
||||||
ProjectModel.instance.setMetaData('dbConfigSchema', this.configSchema);
|
ProjectModel.instance.setMetaData('dbConfigSchema', this.configSchema);
|
||||||
|
|
||||||
|
const versionNumbers = this.parseServerVersion?.split(".")
|
||||||
|
if (versionNumbers && versionNumbers.length > 0) {
|
||||||
|
// Let's only save the major version number,
|
||||||
|
// since this will be used to determine which verison of the API to use.
|
||||||
|
ProjectModel.instance.setMetaData('dbVersionMajor', versionNumbers[0]);
|
||||||
|
} else {
|
||||||
|
ProjectModel.instance.setMetaData('dbVersionMajor', undefined);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ProjectModel.instance.setMetaData('dbCollections', undefined);
|
ProjectModel.instance.setMetaData('dbCollections', undefined);
|
||||||
ProjectModel.instance.setMetaData('systemCollections', undefined);
|
ProjectModel.instance.setMetaData('systemCollections', undefined);
|
||||||
ProjectModel.instance.setMetaData('dbConfigSchema', undefined);
|
ProjectModel.instance.setMetaData('dbConfigSchema', undefined);
|
||||||
|
ProjectModel.instance.setMetaData('dbVersionMajor', undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { usePluginContext } from '@noodl-contexts/PluginContext';
|
import React, { RefObject } from 'react';
|
||||||
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';
|
||||||
@@ -54,6 +55,7 @@ export function DeployPopup(props: DeployPopupProps) {
|
|||||||
onClose={props.onClose}
|
onClose={props.onClose}
|
||||||
hasArrow
|
hasArrow
|
||||||
isLockingScroll
|
isLockingScroll
|
||||||
|
alwaysMounted
|
||||||
>
|
>
|
||||||
<DeployPopupChild />
|
<DeployPopupChild />
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
@@ -62,6 +64,19 @@ export function DeployPopup(props: DeployPopupProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FluxscapeDeployTab() {
|
function FluxscapeDeployTab() {
|
||||||
// Preview URL: 'http://192.168.0.33:8574/'
|
const params = {};
|
||||||
return <iframe src="https://portal.fluxscape.io" style={{ width: "100%", height: "50vh", borderStyle: "none" }} />;
|
|
||||||
|
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' }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
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';
|
||||||
|
|
||||||
@@ -118,12 +121,21 @@ export function CloudServiceCard({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isEditorEnvironment && (
|
{isEditorEnvironment && (
|
||||||
<PrimaryButton
|
<Tooltip
|
||||||
label="Open dashboard"
|
content="Open the Parse Dashboard"
|
||||||
size={PrimaryButtonSize.Small}
|
fineType={[
|
||||||
onClick={onDashboardClicked}
|
`In Window: ${Keybindings.CLOUD_SERVICE_OPEN_DASHBOARD.label}`,
|
||||||
isGrowing
|
`In Browser: ${Keybindings.CLOUD_SERVICE_OPEN_DASHBOARD_BROWSER.label}`
|
||||||
/>
|
]}
|
||||||
|
renderDirection={DialogRenderDirection.Below}
|
||||||
|
>
|
||||||
|
<PrimaryButton
|
||||||
|
label="Open dashboard"
|
||||||
|
size={PrimaryButtonSize.Small}
|
||||||
|
onClick={onDashboardClicked}
|
||||||
|
isGrowing
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ function BaseVersionControlPanel() {
|
|||||||
|
|
||||||
export function VersionControlPanel() {
|
export function VersionControlPanel() {
|
||||||
const [git, setGit] = useState<Git>(null);
|
const [git, setGit] = useState<Git>(null);
|
||||||
|
const [state, setState] = useState<'loading' | 'loaded' | 'not-git'>('loading');
|
||||||
|
|
||||||
async function createGit() {
|
async function createGit() {
|
||||||
const gitClient = new Git(mergeProject);
|
const gitClient = new Git(mergeProject);
|
||||||
@@ -206,12 +207,18 @@ export function VersionControlPanel() {
|
|||||||
setGit(gitClient);
|
setGit(gitClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isGitProject = git === null ? LocalProjectsModel.instance.isGitProject(ProjectModel.instance) : true;
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isGitProject) {
|
LocalProjectsModel.instance
|
||||||
createGit();
|
.isGitProject(ProjectModel.instance)
|
||||||
}
|
.then(async (isGitProject) => {
|
||||||
}, [isGitProject]);
|
if (isGitProject) {
|
||||||
|
await createGit();
|
||||||
|
setState('loaded');
|
||||||
|
} else {
|
||||||
|
setState('not-git');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
async function setupGit() {
|
async function setupGit() {
|
||||||
const gitClient = new Git(mergeProject);
|
const gitClient = new Git(mergeProject);
|
||||||
@@ -220,7 +227,7 @@ export function VersionControlPanel() {
|
|||||||
setGit(gitClient);
|
setGit(gitClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (git === null && !isGitProject) {
|
if (git === null && state === 'not-git') {
|
||||||
return (
|
return (
|
||||||
<BasePanel isFill title="Version Control">
|
<BasePanel isFill title="Version Control">
|
||||||
<Box hasXSpacing hasYSpacing>
|
<Box hasXSpacing hasYSpacing>
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import { Commit } from '@noodl/git/src/core/models/snapshot';
|
|||||||
import { FileChange } from '@noodl/git/src/core/models/status';
|
import { FileChange } from '@noodl/git/src/core/models/status';
|
||||||
import { revRange } from '@noodl/git/src/core/rev-list';
|
import { revRange } from '@noodl/git/src/core/rev-list';
|
||||||
|
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
|
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
|
||||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||||
import { ProjectDiff, diffProject } from '@noodl-utils/projectmerger.diff';
|
import { ProjectDiff, diffProject } from '@noodl-utils/projectmerger.diff';
|
||||||
|
|
||||||
import { useVersionControlContext } from '../context';
|
import { useVersionControlContext } from '../context';
|
||||||
|
import { getProjectFilePath } from '../context/DiffUtils';
|
||||||
import { DiffList } from './DiffList';
|
import { DiffList } from './DiffList';
|
||||||
|
|
||||||
//Kind:
|
//Kind:
|
||||||
@@ -124,7 +126,10 @@ async function getMergeDiff(repositoryPath: string, commit: Commit, refToDiffTo:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getProjectFile(commit: Commit) {
|
async function getProjectFile(commit: Commit) {
|
||||||
const projectContent = JSON.parse(await commit.getFileAsString('project.json'));
|
const projectFilePath = getProjectFilePath(commit.repositoryDir, ProjectModel.instance._retainedProjectDirectory);
|
||||||
|
|
||||||
|
const projectContentRaw = await commit.getFileAsString(projectFilePath);
|
||||||
|
const projectContent = JSON.parse(projectContentRaw);
|
||||||
applyPatches(projectContent);
|
applyPatches(projectContent);
|
||||||
return projectContent;
|
return projectContent;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export function DiffList({ diff, fileChanges, componentDiffTitle, actions, commi
|
|||||||
const [imageDiff, setImageDiff] = useState<IImageDiff>(null);
|
const [imageDiff, setImageDiff] = useState<IImageDiff>(null);
|
||||||
|
|
||||||
const components = diff?.components ? getChangedComponents(diff.components) : [];
|
const components = diff?.components ? getChangedComponents(diff.components) : [];
|
||||||
const files = (fileChanges || [])?.filter((f) => f.path !== 'project.json') || [];
|
const files = (fileChanges || [])?.filter((f) => !f.path.endsWith('project.json')) || [];
|
||||||
const settings = diff?.settings ? getChangedObjectProperties(diff.settings) : [];
|
const settings = diff?.settings ? getChangedObjectProperties(diff.settings) : [];
|
||||||
const colorStyles = diff?.styles.colors ? getChangedObjectProperties(diff.styles.colors) : [];
|
const colorStyles = diff?.styles.colors ? getChangedObjectProperties(diff.styles.colors) : [];
|
||||||
const textStyles = diff?.styles.text ? getChangedObjectProperties(diff.styles.text) : [];
|
const textStyles = diff?.styles.text ? getChangedObjectProperties(diff.styles.text) : [];
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import { getCommit } from '@noodl/git/src/core/logs';
|
||||||
|
import { FileStatusKind } from '@noodl/git/src/core/models/status';
|
||||||
|
|
||||||
|
import { FeedbackType } from '@noodl-constants/FeedbackType';
|
||||||
|
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
|
||||||
import {
|
import {
|
||||||
ProjectDiff,
|
ProjectDiff,
|
||||||
ProjectDiffItem,
|
ProjectDiffItem,
|
||||||
ProjectBasicDiffItem,
|
ProjectBasicDiffItem,
|
||||||
ArrayDiff,
|
ArrayDiff,
|
||||||
diffProject
|
diffProject,
|
||||||
|
createEmptyArrayDiff
|
||||||
} from '@noodl-utils/projectmerger.diff';
|
} from '@noodl-utils/projectmerger.diff';
|
||||||
|
|
||||||
|
import { IconName } from '@noodl-core-ui/components/common/Icon';
|
||||||
|
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
|
||||||
|
|
||||||
import { ProjectModel } from '../../../../models/projectmodel';
|
import { ProjectModel } from '../../../../models/projectmodel';
|
||||||
|
|
||||||
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
|
export interface ProjectLocalDiff extends ProjectDiff {
|
||||||
import { FileStatusKind } from '@noodl/git/src/core/models/status';
|
|
||||||
import { IconName } from '@noodl-core-ui/components/common/Icon';
|
|
||||||
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
|
|
||||||
import { FeedbackType } from '@noodl-constants/FeedbackType';
|
|
||||||
import { getCommit } from '@noodl/git/src/core/logs';
|
|
||||||
|
|
||||||
export interface ProjectLocalDiff extends ProjectDiff{
|
|
||||||
baseProject: TSFixme; //Project model as an object from raw json
|
baseProject: TSFixme; //Project model as an object from raw json
|
||||||
commitShaDiffedTo: string;
|
commitShaDiffedTo: string;
|
||||||
}
|
}
|
||||||
@@ -84,17 +87,49 @@ export function getFileStatusIconProps(status: FileStatusKind): Partial<ListItem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doLocalDiff(repositoryPath: string, headCommitId: string): Promise<ProjectLocalDiff> {
|
export function getProjectFilePath(repositoryPath: string, projectPath: string) {
|
||||||
const baseCommit = await getCommit(repositoryPath, headCommitId);
|
const relativePath = path.relative(repositoryPath, projectPath);
|
||||||
const baseProjectJson = await baseCommit.getFileAsString('project.json');
|
const projectFilePath = path.join(relativePath, 'project.json').replaceAll('\\', '/');
|
||||||
const baseProject = JSON.parse(baseProjectJson);
|
return projectFilePath;
|
||||||
applyPatches(baseProject);
|
}
|
||||||
|
|
||||||
const diff = diffProject(baseProject, ProjectModel.instance.toJSON());
|
export async function doLocalDiff(
|
||||||
|
repositoryPath: string,
|
||||||
return {
|
projectPath: string,
|
||||||
...diff,
|
headCommitId: string
|
||||||
baseProject,
|
): Promise<ProjectLocalDiff> {
|
||||||
commitShaDiffedTo: headCommitId
|
const projectFilePath = getProjectFilePath(repositoryPath, projectPath);
|
||||||
};
|
|
||||||
|
try {
|
||||||
|
const baseCommit = await getCommit(projectPath, headCommitId);
|
||||||
|
const baseProjectJson = await baseCommit.getFileAsString(projectFilePath);
|
||||||
|
const baseProject = JSON.parse(baseProjectJson);
|
||||||
|
applyPatches(baseProject);
|
||||||
|
|
||||||
|
const diff = diffProject(baseProject, ProjectModel.instance.toJSON());
|
||||||
|
|
||||||
|
return {
|
||||||
|
...diff,
|
||||||
|
baseProject,
|
||||||
|
commitShaDiffedTo: headCommitId
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (error.toString().includes('exists on disk, but not in')) {
|
||||||
|
console.warn('project.json does not exist in this commit.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return empty state
|
||||||
|
return {
|
||||||
|
baseProject: {},
|
||||||
|
commitShaDiffedTo: headCommitId,
|
||||||
|
components: createEmptyArrayDiff(),
|
||||||
|
variants: createEmptyArrayDiff(),
|
||||||
|
settings: createEmptyArrayDiff(),
|
||||||
|
styles: {
|
||||||
|
colors: createEmptyArrayDiff(),
|
||||||
|
text: createEmptyArrayDiff()
|
||||||
|
},
|
||||||
|
cloudservices: createEmptyArrayDiff()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Slot } from '@noodl-core-ui/types/global';
|
|||||||
import { doLocalDiff, ProjectLocalDiff } from './DiffUtils';
|
import { doLocalDiff, ProjectLocalDiff } from './DiffUtils';
|
||||||
import { useVersionControlFetch } from './fetch.context';
|
import { useVersionControlFetch } from './fetch.context';
|
||||||
import { BranchStatus, IVersionControlContext } from './types';
|
import { BranchStatus, IVersionControlContext } from './types';
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
|
|
||||||
const VersionControlContext = createContext<IVersionControlContext>({
|
const VersionControlContext = createContext<IVersionControlContext>({
|
||||||
git: null,
|
git: null,
|
||||||
@@ -57,7 +58,8 @@ export function VersionControlProvider({ git, children }: { git: Git; children:
|
|||||||
(async () => {
|
(async () => {
|
||||||
const currentCommitSha = await git.getHeadCommitId();
|
const currentCommitSha = await git.getHeadCommitId();
|
||||||
if (currentCommitSha) {
|
if (currentCommitSha) {
|
||||||
const diff = await doLocalDiff(git.repositoryPath, currentCommitSha);
|
const projectPath = ProjectModel.instance._retainedProjectDirectory;
|
||||||
|
const diff = await doLocalDiff(git.repositoryPath, projectPath, currentCommitSha);
|
||||||
setLocalDiff(diff);
|
setLocalDiff(diff);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { useSidePanelKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { ComponentModel } from '@noodl-models/componentmodel';
|
||||||
|
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
||||||
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
|
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
|
||||||
import { performSearch } from '@noodl-utils/universal-search';
|
import { performSearch } from '@noodl-utils/universal-search';
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ export function SearchPanel() {
|
|||||||
}
|
}
|
||||||
}, [debouncedSearchTerm]);
|
}, [debouncedSearchTerm]);
|
||||||
|
|
||||||
function onSearchItemClicked(searchResult) {
|
function onSearchItemClicked(searchResult: SearchResultItem) {
|
||||||
if (searchResult.type === 'Component') {
|
if (searchResult.type === 'Component') {
|
||||||
NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, {
|
NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, {
|
||||||
breadcrumbs: false,
|
breadcrumbs: false,
|
||||||
@@ -85,29 +87,7 @@ export function SearchPanel() {
|
|||||||
|
|
||||||
<div className={css.SearchResults}>
|
<div className={css.SearchResults}>
|
||||||
{searchResults.map((component) => (
|
{searchResults.map((component) => (
|
||||||
<Section
|
<SearchItem key={component.componentId} component={component} onSearchItemClicked={onSearchItemClicked} />
|
||||||
title={`${component.componentName} (${component.results.length} result${
|
|
||||||
component.results.length > 1 ? 's' : ''
|
|
||||||
})`}
|
|
||||||
key={component.componentId}
|
|
||||||
variant={SectionVariant.Panel}
|
|
||||||
>
|
|
||||||
{component.results.map((result, index) => (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
css.SearchResultItem
|
|
||||||
// lastActiveComponentId === result.componentTarget.id && css['is-active']
|
|
||||||
)}
|
|
||||||
key={index}
|
|
||||||
onClick={() => onSearchItemClicked(result)}
|
|
||||||
>
|
|
||||||
<Label variant={TextType.Proud}>
|
|
||||||
{result.userLabel ? result.type + ' - ' + result.userLabel : result.type}
|
|
||||||
</Label>
|
|
||||||
<Text>{result.label}</Text>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Section>
|
|
||||||
))}
|
))}
|
||||||
{searchResults.length === 0 && debouncedSearchTerm.length > 0 && (
|
{searchResults.length === 0 && debouncedSearchTerm.length > 0 && (
|
||||||
<Container hasXSpacing hasYSpacing>
|
<Container hasXSpacing hasYSpacing>
|
||||||
@@ -118,3 +98,51 @@ export function SearchPanel() {
|
|||||||
</BasePanel>
|
</BasePanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchResultItem = {
|
||||||
|
componentTarget: ComponentModel;
|
||||||
|
label: string;
|
||||||
|
nodeTarget: NodeGraphNode;
|
||||||
|
type: string;
|
||||||
|
userLabel: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SearchItemProps = {
|
||||||
|
component: {
|
||||||
|
componentName: string;
|
||||||
|
componentId: string;
|
||||||
|
results: SearchResultItem[];
|
||||||
|
};
|
||||||
|
onSearchItemClicked: (item: SearchResultItem) => 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
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Noodl Viewer</title>
|
<title>Fluxscape 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>
|
||||||
|
|||||||
@@ -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 || 'Noodl Viewer');
|
injected = injected.replace('{{#title#}}', settings.htmlTitle || 'Fluxscape Viewer');
|
||||||
injected = injected.replace('{{#customHeadCode#}}', settings.headCode || '');
|
injected = injected.replace('{{#customHeadCode#}}', settings.headCode || '');
|
||||||
|
|
||||||
response.writeHead(200, {
|
response.writeHead(200, {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "Noodl <info@noodl.net>",
|
"author": "Fluxscape <contact@fluxscape.io>",
|
||||||
"homepage": "https://noodl.net",
|
"homepage": "https://fluxscape.io",
|
||||||
"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",
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ export async function open(basePath: string): Promise<string> {
|
|||||||
// console.log("VCS error when opening project: " + e);
|
// console.log("VCS error when opening project: " + e);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return basePath;
|
|
||||||
|
// Find the relative git repository path
|
||||||
|
const repositoryPath = await getTopLevelWorkingDirectory(basePath);
|
||||||
|
|
||||||
|
return repositoryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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": "Noodl <info@noodl.net>",
|
"author": "Fluxscape <contact@fluxscape.io>",
|
||||||
"homepage": "https://noodl.net",
|
"homepage": "https://fluxscape.io",
|
||||||
"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"
|
||||||
|
|||||||
@@ -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": "Noodl <info@noodl.net>",
|
"author": "Fluxscape <contact@fluxscape.io>",
|
||||||
"homepage": "https://noodl.net",
|
"homepage": "https://fluxscape.io",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:coverage": "jest --coverage"
|
"test:coverage": "jest --coverage"
|
||||||
|
|||||||
@@ -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": "Noodl <info@noodl.net>",
|
"author": "Fluxscape <contact@fluxscape.io>",
|
||||||
"homepage": "https://noodl.net"
|
"homepage": "https://fluxscape.io"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,15 @@ class CloudStore {
|
|||||||
|
|
||||||
_initCloudServices() {
|
_initCloudServices() {
|
||||||
_collections = undefined; // clear collection cache, so it's refetched
|
_collections = undefined; // clear collection cache, so it's refetched
|
||||||
const cloudServices = NoodlRuntime.instance.getMetaData('cloudservices');
|
|
||||||
|
|
||||||
|
const cloudServices = NoodlRuntime.instance.getMetaData('cloudservices');
|
||||||
if (cloudServices) {
|
if (cloudServices) {
|
||||||
this.appId = cloudServices.appId;
|
this.appId = cloudServices.appId;
|
||||||
this.endpoint = cloudServices.endpoint;
|
this.endpoint = cloudServices.endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dbVersionMajor = NoodlRuntime.instance.getMetaData('dbVersionMajor');
|
||||||
|
this.dbVersionMajor = dbVersionMajor;
|
||||||
}
|
}
|
||||||
|
|
||||||
on() {
|
on() {
|
||||||
@@ -168,13 +171,10 @@ class CloudStore {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
|
|
||||||
if (options.limit) args.push('limit=' + options.limit);
|
if (options.limit) args.push('limit=' + options.limit);
|
||||||
if (options.skip) args.push('skip=' + options.skip);
|
if (options.skip) args.push('skip=' + options.skip);
|
||||||
|
|
||||||
const grouping = {
|
const grouping = {};
|
||||||
objectId: null
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(options.group).forEach((k) => {
|
Object.keys(options.group).forEach((k) => {
|
||||||
const _g = {};
|
const _g = {};
|
||||||
@@ -188,7 +188,20 @@ class CloudStore {
|
|||||||
grouping[k] = _g;
|
grouping[k] = _g;
|
||||||
});
|
});
|
||||||
|
|
||||||
args.push('group=' + JSON.stringify(grouping));
|
// I don't know which version the API was changed, lets just say above 4 for now.
|
||||||
|
if (this.dbVersionMajor && this.dbVersionMajor > 4) {
|
||||||
|
grouping._id = null;
|
||||||
|
|
||||||
|
if (options.where) args.push('$match=' + encodeURIComponent(JSON.stringify(options.where)));
|
||||||
|
|
||||||
|
args.push('$group=' + JSON.stringify(grouping));
|
||||||
|
} else {
|
||||||
|
grouping.objectId = null;
|
||||||
|
|
||||||
|
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
|
||||||
|
|
||||||
|
args.push('group=' + JSON.stringify(grouping));
|
||||||
|
}
|
||||||
|
|
||||||
this._makeRequest('/aggregate/' + options.collection + (args.length > 0 ? '?' + args.join('&') : ''), {
|
this._makeRequest('/aggregate/' + options.collection + (args.length > 0 ? '?' + args.join('&') : ''), {
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
async query(className, query, options) {
|
async query(className, query, options) {
|
||||||
|
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().query({
|
cloudstore().query({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -39,6 +40,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async count(className, query) {
|
async count(className, query) {
|
||||||
|
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().count({
|
cloudstore().count({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -60,6 +62,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async distinct(className, property, query) {
|
async distinct(className, property, query) {
|
||||||
|
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().distinct({
|
cloudstore().distinct({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -82,6 +85,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async aggregate(className, group, query) {
|
async aggregate(className, group, query) {
|
||||||
|
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().aggregate({
|
cloudstore().aggregate({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -104,6 +108,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -126,6 +131,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async increment(objectOrId, properties, options) {
|
async increment(objectOrId, properties, options) {
|
||||||
|
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
||||||
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
||||||
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
||||||
|
|
||||||
@@ -149,6 +155,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async save(objectOrId, properties, options) {
|
async save(objectOrId, properties, options) {
|
||||||
|
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
||||||
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
||||||
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
||||||
|
|
||||||
@@ -179,6 +186,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async create(className, properties, options) {
|
async create(className, properties, options) {
|
||||||
|
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().create({
|
cloudstore().create({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -197,6 +205,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async delete(objectOrId, options) {
|
async delete(objectOrId, options) {
|
||||||
|
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
||||||
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
||||||
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
||||||
|
|
||||||
@@ -265,7 +274,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
reject(Error(rr || 'Failed to add relation.'));
|
reject(Error(err || 'Failed to add relation.'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,12 +75,12 @@ 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({
|
||||||
collection: internal.collectionId,
|
collection: internal.collectionId,
|
||||||
objectId: model.getId(), // Get the objectId part of the model id
|
objectId: model.getId(), // Get the objectId part of the model id
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const DateToStringNode = {
|
|||||||
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);
|
||||||
@@ -68,6 +69,7 @@ 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);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ const StringFormatDefinition = {
|
|||||||
name: 'String Format',
|
name: 'String Format',
|
||||||
docs: 'https://docs.noodl.net/nodes/string-manipulation/string-format',
|
docs: 'https://docs.noodl.net/nodes/string-manipulation/string-format',
|
||||||
category: 'String Manipulation',
|
category: 'String Manipulation',
|
||||||
|
usePortAsLabel: 'format',
|
||||||
|
portLabelTruncationMode: 'length',
|
||||||
initialize() {
|
initialize() {
|
||||||
const internal = this._internal;
|
const internal = this._internal;
|
||||||
internal.format = '';
|
internal.format = '';
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
//this just assumes the base url is '/' always
|
//this just assumes the base url is '/' always
|
||||||
function getAbsoluteUrl(_url) {
|
function getAbsoluteUrl(_url) {
|
||||||
|
|
||||||
//convert to string in case the _url is a Cloud File (which is an object with a custom toString())
|
//convert to string in case the _url is a Cloud File (which is an object with a custom toString())
|
||||||
const url = String(_url);
|
const url = String(_url);
|
||||||
|
|
||||||
//only add a the base url if this is a local URL (e.g. not a https url or base64 string)
|
//only add a the base url if this is a local URL (e.g. not a https url or base64 string)
|
||||||
if (!url || url[0] === "/" || url.includes("://") || url.startsWith('data:')) {
|
if (!url || url[0] === '/' || url.includes('://') || url.startsWith('data:')) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Noodl.baseUrl || '/') + url;
|
return (Noodl.Env['BaseUrl'] || '/') + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log an error thrown by the JavaScript nodes.
|
* Log an error thrown by the JavaScript nodes.
|
||||||
*
|
*
|
||||||
* @param {any} error
|
* @param {any} error
|
||||||
*/
|
*/
|
||||||
function logJavaScriptNodeError(error) {
|
function logJavaScriptNodeError(error) {
|
||||||
if (typeof error === 'string') {
|
if (typeof error === 'string') {
|
||||||
|
|||||||
@@ -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": "Noodl <info@noodl.net>",
|
"author": "Fluxscape <contact@fluxscape.io>",
|
||||||
"homepage": "https://noodl.net"
|
"homepage": "https://fluxscape.io"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@noodl/cloud-runtime",
|
"name": "@noodl/cloud-runtime",
|
||||||
"author": "Noodl <info@noodl.net>",
|
"author": "Fluxscape <contact@fluxscape.io>",
|
||||||
"homepage": "https://noodl.net",
|
"homepage": "https://fluxscape.io",
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
|
|||||||
@@ -74,6 +74,59 @@ 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
|
||||||
@@ -115,15 +168,17 @@ declare namespace Noodl {
|
|||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
query(
|
query<TClassName extends RecordClassName>(
|
||||||
className: RecordClassName,
|
className: TClassName,
|
||||||
query?: any,
|
query?:
|
||||||
|
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[];
|
sort?: string | RecordSortKey<keyof DatabaseSchema[TClassName]>;
|
||||||
include?: any;
|
include?: string | (keyof DatabaseSchema[TClassName])[];
|
||||||
select?: any;
|
select?: string | (keyof DatabaseSchema[TClassName])[];
|
||||||
}
|
}
|
||||||
): Promise<any>;
|
): Promise<any>;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { Noodl, Slot } from '../../../types';
|
|||||||
export interface ButtonProps extends Noodl.ReactProps {
|
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;
|
||||||
|
|
||||||
@@ -96,6 +98,7 @@ export function Button(props: ButtonProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
{...props.attrs}
|
||||||
className={className}
|
className={className}
|
||||||
disabled={!props.enabled}
|
disabled={!props.enabled}
|
||||||
{...Utils.controlEvents(props)}
|
{...Utils.controlEvents(props)}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export interface CheckboxProps extends Noodl.ReactProps {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
useLabel: boolean;
|
useLabel: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
labelSpacing: string;
|
labelSpacing: string;
|
||||||
@@ -47,6 +49,7 @@ export function Checkbox(props: CheckboxProps) {
|
|||||||
Layout.align(style, props);
|
Layout.align(style, props);
|
||||||
|
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
className: [props.className, 'ndl-controls-checkbox-2'].join(' '),
|
className: [props.className, 'ndl-controls-checkbox-2'].join(' '),
|
||||||
disabled: !props.enabled,
|
disabled: !props.enabled,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export interface RadioButtonProps extends Noodl.ReactProps {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
value: string;
|
value: string;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
useLabel: boolean;
|
useLabel: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
labelSpacing: string;
|
labelSpacing: string;
|
||||||
@@ -47,6 +49,7 @@ export function RadioButton(props: RadioButtonProps) {
|
|||||||
props.checkedChanged && props.checkedChanged(radioButtonGroup ? radioButtonGroup.selected === props.value : false);
|
props.checkedChanged && props.checkedChanged(radioButtonGroup ? radioButtonGroup.selected === props.value : false);
|
||||||
|
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
disabled: !props.enabled,
|
disabled: !props.enabled,
|
||||||
className: [props.className, 'ndl-controls-radio-2'].join(' '),
|
className: [props.className, 'ndl-controls-radio-2'].join(' '),
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export interface SelectProps extends Noodl.ReactProps {
|
|||||||
textStyle: Noodl.TextStyle;
|
textStyle: Noodl.TextStyle;
|
||||||
items: TSFixme;
|
items: TSFixme;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
placeholderOpacity: string;
|
placeholderOpacity: string;
|
||||||
|
|
||||||
@@ -81,6 +83,7 @@ export function Select(props: SelectProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
className: props.className,
|
className: props.className,
|
||||||
style: {
|
style: {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export interface SliderProps extends Noodl.ReactProps {
|
|||||||
id: string;
|
id: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
value: number;
|
value: number;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
@@ -103,6 +105,7 @@ export function Slider(props: SliderProps) {
|
|||||||
const className = `ndl-controls-range2 ${instanceClassId} ${props.className ? props.className : ''} `;
|
const className = `ndl-controls-range2 ${instanceClassId} ${props.className ? props.className : ''} `;
|
||||||
|
|
||||||
const inputProps: React.InputHTMLAttributes<HTMLInputElement> = {
|
const inputProps: React.InputHTMLAttributes<HTMLInputElement> = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
style: {
|
style: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export interface TextInputProps extends Noodl.ReactProps {
|
|||||||
type: 'text' | 'textArea' | 'email' | 'number' | 'password' | 'url';
|
type: 'text' | 'textArea' | 'email' | 'number' | 'password' | 'url';
|
||||||
textStyle: Noodl.TextStyle;
|
textStyle: Noodl.TextStyle;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
@@ -149,6 +151,7 @@ export class TextInput extends React.Component<TextInputProps, State> {
|
|||||||
inputStyles.color = props.noodlNode.context.styles.resolveColor(inputStyles.color);
|
inputStyles.color = props.noodlNode.context.styles.resolveColor(inputStyles.color);
|
||||||
|
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
|
...props.attrs,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
value: this.state.value,
|
value: this.state.value,
|
||||||
...Utils.controlEvents(props),
|
...Utils.controlEvents(props),
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export interface ColumnsProps extends Noodl.ReactProps {
|
|||||||
marginX: string;
|
marginX: string;
|
||||||
marginY: string;
|
marginY: string;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
justifyContent: 'flex-start' | 'flex-end' | 'center';
|
justifyContent: 'flex-start' | 'flex-end' | 'center';
|
||||||
direction: 'row' | 'column';
|
direction: 'row' | 'column';
|
||||||
minWidth: string;
|
minWidth: string;
|
||||||
@@ -115,7 +117,10 @@ export function Columns(props: ColumnsProps) {
|
|||||||
|
|
||||||
// ForEachCompoent breaks the layout but is needed to send onMount/onUnmount
|
// ForEachCompoent breaks the layout but is needed to send onMount/onUnmount
|
||||||
if (!Array.isArray(props.children)) {
|
if (!Array.isArray(props.children)) {
|
||||||
children = [props.children];
|
// @ts-expect-error props.children.type is any
|
||||||
|
if (props.children.type !== ForEachComponent) {
|
||||||
|
children = [props.children]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
children = props.children.filter((child) => child.type !== ForEachComponent);
|
children = props.children.filter((child) => child.type !== ForEachComponent);
|
||||||
forEachComponent = props.children.find((child) => child.type === ForEachComponent);
|
forEachComponent = props.children.find((child) => child.type === ForEachComponent);
|
||||||
@@ -123,6 +128,7 @@ 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={{
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ BScroll.use(Slide);
|
|||||||
export interface GroupProps extends Noodl.ReactProps {
|
export interface GroupProps extends Noodl.ReactProps {
|
||||||
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
|
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
scrollSnapEnabled: boolean;
|
scrollSnapEnabled: boolean;
|
||||||
showScrollbar: boolean;
|
showScrollbar: boolean;
|
||||||
scrollEnabled: boolean;
|
scrollEnabled: boolean;
|
||||||
@@ -271,6 +273,7 @@ export class Group extends React.Component<GroupProps> {
|
|||||||
<Component
|
<Component
|
||||||
// @ts-expect-error Lets hope that the type passed here is always static!
|
// @ts-expect-error Lets hope that the type passed here is always static!
|
||||||
className={props.className}
|
className={props.className}
|
||||||
|
{...props.attrs}
|
||||||
{...props.dom}
|
{...props.dom}
|
||||||
{...PointerListeners(props)}
|
{...PointerListeners(props)}
|
||||||
style={style}
|
style={style}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface ImageProps extends Noodl.ReactProps {
|
|||||||
src: string;
|
src: string;
|
||||||
onLoad?: () => void;
|
onLoad?: () => void;
|
||||||
};
|
};
|
||||||
|
attrs: React.Attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Image(props: ImageProps) {
|
export function Image(props: ImageProps) {
|
||||||
@@ -30,5 +31,5 @@ export function Image(props: ImageProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <img className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />;
|
return <img {...props.attrs} className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { Noodl } from '../../../types';
|
|||||||
export interface TextProps extends Noodl.ReactProps {
|
export interface TextProps extends Noodl.ReactProps {
|
||||||
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
|
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
|
||||||
|
|
||||||
|
attrs: React.Attributes;
|
||||||
|
|
||||||
textStyle: Noodl.TextStyle;
|
textStyle: Noodl.TextStyle;
|
||||||
text: string;
|
text: string;
|
||||||
|
|
||||||
@@ -48,6 +50,7 @@ export function Text(props: TextProps) {
|
|||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
className={['ndl-visual-text', props.className].join(' ')}
|
className={['ndl-visual-text', props.className].join(' ')}
|
||||||
|
{...props.attrs}
|
||||||
{...props.dom}
|
{...props.dom}
|
||||||
{...PointerListeners(props)}
|
{...PointerListeners(props)}
|
||||||
style={style}
|
style={style}
|
||||||
|
|||||||
@@ -30,11 +30,24 @@ const ButtonNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.layout = 'row'; //Used to tell child nodes what layout to expect
|
this.props.layout = 'row'; //Used to tell child nodes what layout to expect
|
||||||
},
|
},
|
||||||
getReactComponent() {
|
getReactComponent() {
|
||||||
return Button;
|
return Button;
|
||||||
},
|
},
|
||||||
|
inputs: {
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
inputCss: {
|
inputCss: {
|
||||||
backgroundColor: {
|
backgroundColor: {
|
||||||
index: 100,
|
index: 100,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const CheckBoxNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.sizeMode = 'explicit';
|
this.props.sizeMode = 'explicit';
|
||||||
this.props.id = 'input-' + guid();
|
this.props.id = 'input-' + guid();
|
||||||
this.props.checked = this._internal.checked = false;
|
this.props.checked = this._internal.checked = false;
|
||||||
@@ -94,6 +95,16 @@ const CheckBoxNode = {
|
|||||||
this.flagOutputDirty('checked');
|
this.flagOutputDirty('checked');
|
||||||
this._updateVisualState();
|
this._updateVisualState();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputCss: {
|
inputCss: {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const OptionsNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
|
this.props.attrs = {};
|
||||||
this._itemsChanged = () => {
|
this._itemsChanged = () => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
@@ -90,6 +91,16 @@ const OptionsNode = {
|
|||||||
this.flagOutputDirty('value');
|
this.flagOutputDirty('value');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputProps: {
|
inputProps: {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const RadioButtonNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.sizeMode = 'explicit';
|
this.props.sizeMode = 'explicit';
|
||||||
this.props.id = 'input-' + guid();
|
this.props.id = 'input-' + guid();
|
||||||
|
|
||||||
@@ -61,6 +62,16 @@ const RadioButtonNode = {
|
|||||||
set(value) {
|
set(value) {
|
||||||
this.setStyle({ backgroundColor: value }, 'fill');
|
this.setStyle({ backgroundColor: value }, 'fill');
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputProps: {
|
inputProps: {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const RangeNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.sizeMode = 'contentHeight';
|
this.props.sizeMode = 'contentHeight';
|
||||||
this.props.id = 'input-' + guid();
|
this.props.id = 'input-' + guid();
|
||||||
|
|
||||||
@@ -67,6 +68,16 @@ const RangeNode = {
|
|||||||
set(value) {
|
set(value) {
|
||||||
this._setInputValue(value);
|
this._setInputValue(value);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const TextInputNode = {
|
|||||||
return TextInput;
|
return TextInput;
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.startValue = '';
|
this.props.startValue = '';
|
||||||
this.props.id = this._internal.controlId = 'input-' + guid();
|
this.props.id = this._internal.controlId = 'input-' + guid();
|
||||||
},
|
},
|
||||||
@@ -167,6 +168,16 @@ const TextInputNode = {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputCss: {
|
inputCss: {
|
||||||
|
|||||||
@@ -72,8 +72,10 @@ const Navigate = {
|
|||||||
backCallback: (action, results) => {
|
backCallback: (action, results) => {
|
||||||
this._internal.backResults = results;
|
this._internal.backResults = results;
|
||||||
|
|
||||||
for (var key in results) {
|
for (const key in results) {
|
||||||
if (this.hasOutput('backResult-' + key)) this.flagOutputDirty('backResult-' + key);
|
if (this.hasOutput('backResult-' + key)) {
|
||||||
|
this.flagOutputDirty('backResult-' + key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action !== undefined) this.sendSignalOnOutput(action);
|
if (action !== undefined) this.sendSignalOnOutput(action);
|
||||||
@@ -114,22 +116,23 @@ const Navigate = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'target')
|
if (name === 'target') {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setTargetPageId.bind(this)
|
set: this.setTargetPageId.bind(this)
|
||||||
});
|
});
|
||||||
else if (name === 'transition')
|
} else if (name === 'transition') {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setTransition.bind(this)
|
set: this.setTransition.bind(this)
|
||||||
});
|
});
|
||||||
else if (name.startsWith('tr-'))
|
} else if (name.startsWith('tr-')) {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setTransitionParam.bind(this, name.substring('tr-'.length))
|
set: this.setTransitionParam.bind(this, name.substring('tr-'.length))
|
||||||
});
|
});
|
||||||
else if (name.startsWith('pm-'))
|
} else if (name.startsWith('pm-')) {
|
||||||
return this.registerInput(name, {
|
return this.registerInput(name, {
|
||||||
set: this.setPageParam.bind(this, name.substring('pm-'.length))
|
set: this.setPageParam.bind(this, name.substring('pm-'.length))
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
registerOutputIfNeeded: function (name) {
|
registerOutputIfNeeded: function (name) {
|
||||||
if (this.hasOutput(name)) {
|
if (this.hasOutput(name)) {
|
||||||
@@ -174,18 +177,24 @@ function setup(context, graphModel) {
|
|||||||
if (Transitions[transition]) ports = ports.concat(Transitions[transition].ports(node.parameters));
|
if (Transitions[transition]) ports = ports.concat(Transitions[transition].ports(node.parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(node.parameters['stack'] !== undefined) {
|
const pageStacks = graphModel.getNodesWithType('Page Stack');
|
||||||
var pageStacks = graphModel.getNodesWithType('Page Stack');
|
const pageStack = pageStacks.find(
|
||||||
var pageStack = pageStacks.find(
|
|
||||||
(ps) => (ps.parameters['name'] || 'Main') === (node.parameters['stack'] || 'Main')
|
(ps) => (ps.parameters['name'] || 'Main') === (node.parameters['stack'] || 'Main')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pageStack !== undefined) {
|
if (pageStack !== undefined) {
|
||||||
var pages = pageStack.parameters['pages'];
|
const pages = pageStack.parameters['pages'];
|
||||||
if (pages !== undefined && pages.length > 0) {
|
if (pages !== undefined && pages.length > 0) {
|
||||||
ports.push({
|
ports.push({
|
||||||
plug: 'input',
|
plug: 'input',
|
||||||
type: { name: 'enum', enums: pages.map((p) => ({ label: p.label, value: p.id })), allowEditOnly: true },
|
type: {
|
||||||
|
name: 'enum',
|
||||||
|
enums: pages.map((p) => ({
|
||||||
|
label: p.label,
|
||||||
|
value: p.id
|
||||||
|
})),
|
||||||
|
allowEditOnly: true
|
||||||
|
},
|
||||||
group: 'General',
|
group: 'General',
|
||||||
displayName: 'Target Page',
|
displayName: 'Target Page',
|
||||||
name: 'target',
|
name: 'target',
|
||||||
@@ -193,14 +202,14 @@ function setup(context, graphModel) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// See if there is a target page with component
|
// See if there is a target page with component
|
||||||
var targetPageId = node.parameters['target'] || pages[0].id;
|
const targetPageId = node.parameters['target'] || pages[0].id;
|
||||||
var targetComponentName = pageStack.parameters['pageComp-' + targetPageId];
|
const targetComponentName = pageStack.parameters['pageComp-' + targetPageId];
|
||||||
if (targetComponentName !== undefined) {
|
if (targetComponentName !== undefined) {
|
||||||
const component = graphModel.components[targetComponentName];
|
const component = graphModel.components[targetComponentName];
|
||||||
|
|
||||||
if (component !== undefined) {
|
if (component !== undefined) {
|
||||||
// Make all inputs of the component to inputs of this navigation node
|
// Make all inputs of the component to inputs of this navigation node
|
||||||
for (var inputName in component.inputPorts) {
|
for (const inputName in component.inputPorts) {
|
||||||
ports.push({
|
ports.push({
|
||||||
name: 'pm-' + inputName,
|
name: 'pm-' + inputName,
|
||||||
displayName: inputName,
|
displayName: inputName,
|
||||||
@@ -245,7 +254,6 @@ function setup(context, graphModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
context.editorConnection.sendDynamicPorts(node.id, ports);
|
context.editorConnection.sendDynamicPorts(node.id, ports);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const ColumnsNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize() {
|
initialize() { this.props.attrs = {};
|
||||||
this.props.layoutString = '1 2 1';
|
this.props.layoutString = '1 2 1';
|
||||||
this.props.minWidth = 0;
|
this.props.minWidth = 0;
|
||||||
this.props.marginX = 16;
|
this.props.marginX = 16;
|
||||||
@@ -62,6 +62,16 @@ const ColumnsNode = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const GroupNode = {
|
|||||||
groupPriority: ['General', 'Style', 'Events', 'Mounted', 'Hover Events', 'Pointer Events', 'Focus', 'Scroll']
|
groupPriority: ['General', 'Style', 'Events', 'Mounted', 'Hover Events', 'Pointer Events', 'Focus', 'Scroll']
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this._internal = {
|
this._internal = {
|
||||||
scrollElementDuration: 500,
|
scrollElementDuration: 500,
|
||||||
scrollIndexDuration: 500,
|
scrollIndexDuration: 500,
|
||||||
@@ -143,6 +144,16 @@ const GroupNode = {
|
|||||||
valueChangedToTrue() {
|
valueChangedToTrue() {
|
||||||
this.context.setNodeFocused(this, true);
|
this.context.setNodeFocused(this, true);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputProps: {
|
inputProps: {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const ImageNode = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
this.props.default = '';
|
this.props.default = '';
|
||||||
},
|
},
|
||||||
getReactComponent() {
|
getReactComponent() {
|
||||||
@@ -86,6 +87,16 @@ const ImageNode = {
|
|||||||
this.props.dom.src = getAbsoluteUrl(url);
|
this.props.dom.src = getAbsoluteUrl(url);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inputProps: {
|
inputProps: {
|
||||||
@@ -114,6 +125,12 @@ const ImageNode = {
|
|||||||
propPath: 'dom',
|
propPath: 'dom',
|
||||||
type: 'signal',
|
type: 'signal',
|
||||||
group: 'Events'
|
group: 'Events'
|
||||||
|
},
|
||||||
|
onError: {
|
||||||
|
displayName: 'On Error',
|
||||||
|
propPath: 'dom',
|
||||||
|
type: 'signal',
|
||||||
|
group: 'Events'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ const TextNode = {
|
|||||||
nodeDoubleClickAction: {
|
nodeDoubleClickAction: {
|
||||||
focusPort: 'text'
|
focusPort: 'text'
|
||||||
},
|
},
|
||||||
|
initialize() {
|
||||||
|
this.props.attrs = {};
|
||||||
|
},
|
||||||
getReactComponent() {
|
getReactComponent() {
|
||||||
return Text;
|
return Text;
|
||||||
},
|
},
|
||||||
@@ -135,6 +138,16 @@ const TextNode = {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
testId: {
|
||||||
|
index: 100009,
|
||||||
|
displayName: 'Test ID Attribute',
|
||||||
|
group: 'Advanced HTML',
|
||||||
|
type: 'string',
|
||||||
|
set(value) {
|
||||||
|
this.props.attrs["data-testid"] = value;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default {
|
|||||||
group: 'General',
|
group: 'General',
|
||||||
plug: 'input',
|
plug: 'input',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'Noodl Viewer',
|
default: 'Fluxscape Viewer',
|
||||||
tooltip: 'The title that web browsers show',
|
tooltip: 'The title that web browsers show',
|
||||||
ignoreInExport: true
|
ignoreInExport: true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -131,6 +131,59 @@ declare namespace Noodl {
|
|||||||
*/
|
*/
|
||||||
const Events: EventsApi;
|
const Events: EventsApi;
|
||||||
|
|
||||||
|
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
|
||||||
@@ -172,15 +225,17 @@ declare namespace Noodl {
|
|||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
query(
|
query<TClassName extends RecordClassName>(
|
||||||
className: RecordClassName,
|
className: TClassName,
|
||||||
query?: any,
|
query?:
|
||||||
|
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[];
|
sort?: string | RecordSortKey<keyof DatabaseSchema[TClassName]>;
|
||||||
include?: any;
|
include?: string | (keyof DatabaseSchema[TClassName])[];
|
||||||
select?: any;
|
select?: string | (keyof DatabaseSchema[TClassName])[];
|
||||||
}
|
}
|
||||||
): Promise<any>;
|
): Promise<any>;
|
||||||
|
|
||||||
|
|||||||