Compare commits
40 Commits
rf/flowFix
...
v1.72.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4fe71e074 | ||
|
|
fd4e18f62f | ||
|
|
e428662481 | ||
|
|
b796aeef7a | ||
|
|
55eb48c553 | ||
|
|
a43139fe53 | ||
|
|
c4463bb029 | ||
|
|
cc6eaaf473 | ||
|
|
ed25d9f186 | ||
|
|
35ea2b27b1 | ||
|
|
2c1e3b3372 | ||
|
|
4101d587de | ||
|
|
e6ff3ab6cc | ||
|
|
8fc6c39129 | ||
|
|
fcb5cf4d41 | ||
|
|
2679386bf8 | ||
|
|
580388ce19 | ||
|
|
4e6e66d7b1 | ||
|
|
f4d79ee263 | ||
|
|
38fb3450c8 | ||
|
|
94b20d2f5e | ||
|
|
1753cb7da6 | ||
|
|
2a75cd250e | ||
|
|
29f3fe2663 | ||
|
|
4c913dc4b6 | ||
|
|
5c40ff4290 | ||
|
|
2bbe112444 | ||
|
|
90a12f6131 | ||
|
|
f3f95fa865 | ||
|
|
26784464a4 | ||
|
|
c96e2351d9 | ||
|
|
ddb4916a2e | ||
|
|
1bb5ed9ae0 | ||
|
|
b5b32f00b3 | ||
|
|
c06311faf8 | ||
|
|
8a639b6e7d | ||
|
|
05f568fb8c | ||
|
|
e515c70e71 | ||
|
|
6adc875610 | ||
|
|
8a0d1158c4 |
39
CHANGELOG.md
39
CHANGELOG.md
@@ -1,6 +1,45 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## [1.72.0](https://github.com/windmill-labs/windmill/compare/v1.71.0...v1.72.0) (2023-03-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **backend:** get_result_by_id do a downward pass to find node at any depth ([#1249](https://github.com/windmill-labs/windmill/issues/1249)) ([4c913dc](https://github.com/windmill-labs/windmill/commit/4c913dc4b6be03571a015c97a13829adffb61479))
|
||||
* **frontend:** Add app map component ([#1251](https://github.com/windmill-labs/windmill/issues/1251)) ([ed25d9f](https://github.com/windmill-labs/windmill/commit/ed25d9f186d9925f75404cb193a025d8a41c4540))
|
||||
* **frontend:** app splitpanes ([#1248](https://github.com/windmill-labs/windmill/issues/1248)) ([f4d79ee](https://github.com/windmill-labs/windmill/commit/f4d79ee2633e6cdab0fa2410108b31cfa77e10da))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **backend:** improve result retrieval ([c4463bb](https://github.com/windmill-labs/windmill/commit/c4463bb029907f3c8d77abb194f872aae7876bf6))
|
||||
* **backend:** incorrect get_result_by_id for list_result job ([2a75cd2](https://github.com/windmill-labs/windmill/commit/2a75cd250ea5e01849fc8bbb69bf44f147d0acb8))
|
||||
* **cli:** fix workspace option + run script/flow + whoami ([35ea2b2](https://github.com/windmill-labs/windmill/commit/35ea2b27b12159c68c8507ec1f8686028c975387))
|
||||
* **frontend:** background script not showing inputs ([55eb48c](https://github.com/windmill-labs/windmill/commit/55eb48c55332431304cedbf3bcbbbcff61ec3645))
|
||||
* **frontend:** fix table bindings ([2679386](https://github.com/windmill-labs/windmill/commit/2679386bf87a56352269911bd89e52df5ee9f314))
|
||||
* **frontend:** rework app reactivity ([94b20d2](https://github.com/windmill-labs/windmill/commit/94b20d2f5e3b551974c57ea82b6e3dc16e97b9b8))
|
||||
* **frontend:** rework app reactivity ([1753cb7](https://github.com/windmill-labs/windmill/commit/1753cb7da658f47be974c15da82c71a8e19309a6))
|
||||
|
||||
## [1.71.0](https://github.com/windmill-labs/windmill/compare/v1.70.1...v1.71.0) (2023-02-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **backend:** use counter for sleep/execution/pull durations ([e568690](https://github.com/windmill-labs/windmill/commit/e56869092a03fec4703ddd9ef65c89edb8122962))
|
||||
* **cli:** add autocompletions ([287b2db](https://github.com/windmill-labs/windmill/commit/287b2db22f7b56e90bcd0c4727c00096695c2e0d))
|
||||
* **frontend:** App drawer ([#1246](https://github.com/windmill-labs/windmill/issues/1246)) ([8a0d115](https://github.com/windmill-labs/windmill/commit/8a0d1158c4d7e970cb91e1adf4838e5efdbb39ff))
|
||||
* **frontend:** drawer for editing workspace scripts in flows ([6adc875](https://github.com/windmill-labs/windmill/commit/6adc87561070d8aceaba1838008cd7e6be2e2660))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** Add more app custom css ([#1229](https://github.com/windmill-labs/windmill/issues/1229)) ([a4e4d18](https://github.com/windmill-labs/windmill/commit/a4e4d188ad10443dd0b7f104389594efc768dc59))
|
||||
* **frontend:** Add more app custom css ([#1247](https://github.com/windmill-labs/windmill/issues/1247)) ([1bb5ed9](https://github.com/windmill-labs/windmill/commit/1bb5ed9ae01fd7998b06833b6222e5dd5d774d35))
|
||||
* **frontend:** display currently selected filter even if not in list ([42d1cd6](https://github.com/windmill-labs/windmill/commit/42d1cd6456620ba917c560c87d736dc93634adff))
|
||||
* **frontend:** Fix deeply nested move ([#1245](https://github.com/windmill-labs/windmill/issues/1245)) ([a67f10e](https://github.com/windmill-labs/windmill/commit/a67f10eeb6fdb44bbb3a510badcc5ad0ae187a2b))
|
||||
* **frontend:** invisible subgrids have h-0 + app policies fix ([2244e83](https://github.com/windmill-labs/windmill/commit/2244e83b9da803a4cf46ab0825d7cb6cb0e24872))
|
||||
|
||||
## [1.70.1](https://github.com/windmill-labs/windmill/compare/v1.70.0...v1.70.1) (2023-02-27)
|
||||
|
||||
|
||||
|
||||
8
LICENSE
8
LICENSE
@@ -8,5 +8,9 @@ or belonging to one of the below cases:
|
||||
|
||||
The files under backend/ are AGPL Licensed.
|
||||
The files under frontend/ are AGPL Licensed.
|
||||
The files under python-client/ are Apache 2.0 Licensed.
|
||||
The files under community/ are Apache 2.0 Licensed.
|
||||
The files under python-client/ deno-client/ go-client/ are Apache 2.0 Licensed.
|
||||
|
||||
The openapi files, including the OpenFlow spec is Apache 2.0 Licensed.
|
||||
|
||||
All third party components incorporated into the Windmill Software are licensed under the
|
||||
original license provided by the owner of the applicable component.
|
||||
|
||||
@@ -284,6 +284,7 @@ you to have it being synced automatically everyday.
|
||||
| ------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
|
||||
| DATABASE_URL | | The Postgres database url. | All |
|
||||
| DISABLE_NSJAIL | true | Disable Nsjail Sandboxing | Worker |
|
||||
| SERVER_BIND_ADDR | 0.0.0.0 | IP Address on which to bind listening socket | Server |
|
||||
| PORT | 8000 | Exposed port | Server | |
|
||||
| NUM_WORKERS | 3 | The number of worker per Worker instance (set to 1 on Eks to have 1 pod = 1 worker, set to 0 for an API only instance) | Worker |
|
||||
| DISABLE_SERVER | false | Binary would operate as a worker only instance | Worker |
|
||||
|
||||
171
backend/Cargo.lock
generated
171
backend/Cargo.lock
generated
@@ -129,12 +129,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
|
||||
checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"futures-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -337,15 +336,6 @@ dependencies = [
|
||||
"scoped-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
@@ -525,9 +515,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.6"
|
||||
version = "4.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
|
||||
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
@@ -540,9 +530,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.1.0"
|
||||
version = "4.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
||||
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
@@ -704,9 +694,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.14"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -966,9 +956,9 @@ checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60"
|
||||
checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-iter"
|
||||
@@ -1324,9 +1314,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.15"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
|
||||
checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -1678,9 +1668,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -2048,15 +2038,6 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom8"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
@@ -2103,7 +2084,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2147,27 +2127,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
@@ -2516,9 +2475,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
|
||||
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"toml_edit",
|
||||
@@ -2566,7 +2525,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"built",
|
||||
@@ -2584,7 +2543,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-client"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -2598,7 +2557,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-impl"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"heck",
|
||||
@@ -2620,7 +2579,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-macro"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
|
||||
dependencies = [
|
||||
"openapiv3",
|
||||
"proc-macro2",
|
||||
@@ -2994,7 +2953,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
|
||||
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-compiler-core",
|
||||
@@ -3003,24 +2962,20 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
|
||||
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
"bstr",
|
||||
"itertools",
|
||||
"lz4_flex",
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num_enum",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
|
||||
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -3035,7 +2990,6 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
"rustpython-compiler-core",
|
||||
"thiserror",
|
||||
"tiny-keccak",
|
||||
"unic-emoji-char",
|
||||
"unic-ucd-ident",
|
||||
@@ -3688,9 +3642,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "swc_atoms"
|
||||
version = "0.4.37"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f88175a66f5a7c189e752bda520e148317776ecb22c75adc2c2f24c490834bd0"
|
||||
checksum = "5a172f2e444ae1378286cd27ff2a5cb26eadfd7a77c98ccb0edde8992857be1e"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustc-hash",
|
||||
@@ -3702,9 +3656,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_common"
|
||||
version = "0.29.32"
|
||||
version = "0.29.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc8e0e8109b26be70c82d9709562fc88cbcc09e03c2458221cf216c0088dea2"
|
||||
checksum = "a8a75c46065858a37cdda2c1c6fd056986e3b7752d5ec332e91ce312c6a22749"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ast_node",
|
||||
@@ -3729,9 +3683,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_ast"
|
||||
version = "0.96.7"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "621c66e27fbb6cbb6434a4e2b25e439e9a2583cc3419a4a83eba51d16ac0cd7b"
|
||||
checksum = "7b1f6bc913d0f1daf0fa713a64660aed27848e0047ee671ab2206ad602730d1f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"is-macro",
|
||||
@@ -3746,9 +3700,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_parser"
|
||||
version = "0.124.12"
|
||||
version = "0.127.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89b3f472b3dfbfd279de364d2b014459a281824b938e243a8739037c445d6b6c"
|
||||
checksum = "3875db58a1515382c670679062133294e5ec88aff08776f5fb6e4f3c09c0f5f9"
|
||||
dependencies = [
|
||||
"either",
|
||||
"enum_kind",
|
||||
@@ -3967,9 +3921,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.25.0"
|
||||
version = "1.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@@ -3983,7 +3937,7 @@ dependencies = [
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4080,19 +4034,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.5.1"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
|
||||
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.18.1"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
|
||||
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"nom8",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4290,7 +4244,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
[[package]]
|
||||
name = "typify"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
|
||||
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
|
||||
dependencies = [
|
||||
"typify-impl",
|
||||
"typify-macro",
|
||||
@@ -4299,7 +4253,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typify-impl"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
|
||||
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"log",
|
||||
@@ -4317,7 +4271,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typify-macro"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
|
||||
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4465,9 +4419,11 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_names2"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "029df4cc8238cefc911704ff8fa210853a0f3bce2694d8f51181dd41ee0f3301"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/youknowone/unicode_names2.git?tag=v0.6.0+character-alias#4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde"
|
||||
dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
@@ -4756,7 +4712,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4783,7 +4739,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -4838,7 +4794,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
@@ -4853,7 +4809,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -4866,7 +4822,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4891,7 +4847,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4899,7 +4855,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4913,7 +4869,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4925,7 +4881,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4940,7 +4896,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deno_core",
|
||||
@@ -4954,7 +4910,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -4977,7 +4933,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -5091,6 +5047,15 @@ version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf09497b8f8b5ac5d3bb4d05c0a99be20f26fd3d5f2db7b0716e946d5103658"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.70.1"
|
||||
version = "1.72.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -550,21 +550,6 @@
|
||||
},
|
||||
"query": "SELECT * FROM workspace LIMIT $1 OFFSET $2"
|
||||
},
|
||||
"15de975d9be141c9ed9647935a508492aabbbddbf986d5c5c0f0c415293c432d": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, description)\n VALUES ($1, 'g/all/pretty_secret', $2, true, 'This item is secret'), \n ($3, 'g/all/not_secret', $4, false, 'This item is not secret')"
|
||||
},
|
||||
"163f00eb8b1a489d5f382cdba22a5744e88a8e6f1532d7cb02af560f5f5d49f7": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -660,6 +645,27 @@
|
||||
},
|
||||
"query": "SELECT * FROM workspace_settings WHERE workspace_id = $1"
|
||||
},
|
||||
"176afff6dfceac701d499663f15fb7f569fd9d449406df4c5ecc811414429d42": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "flow_status",
|
||||
"ordinal": 0,
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT flow_status FROM completed_job WHERE id = $1 AND workspace_id = $2 \n UNION ALL SELECT flow_status FROM queue WHERE id = $1 AND workspace_id = $2 "
|
||||
},
|
||||
"187a27d18f78f068319a0ec684f71a76c49dd09c5c74c2c777e35233c4f5a281": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -1183,33 +1189,6 @@
|
||||
},
|
||||
"query": "UPDATE script SET archived = true WHERE hash = $1 AND workspace_id = $2"
|
||||
},
|
||||
"2be0cfd075df9624ccbcbe5fd645e0a5c25460c2d01493f86dcdd9b2b71f6181": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "flow_status",
|
||||
"ordinal": 0,
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"name": "parent_job",
|
||||
"ordinal": 1,
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
null,
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT flow_status, parent_job FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT flow_status, parent_job FROM queue WHERE id = $1 AND workspace_id = $2 "
|
||||
},
|
||||
"2e4115bb2e6c8c85ad1492ad135d6b0454b342126cb5fa17e58caf71b32ee755": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -3678,6 +3657,33 @@
|
||||
},
|
||||
"query": "SELECT is_admin FROM usr where username = $1 AND workspace_id = $2 AND disabled = false"
|
||||
},
|
||||
"92ce38eaaef9e793a49788bf3b7fc88340e83da57b785364a477f08c776f66f3": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "flow_status",
|
||||
"ordinal": 0,
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"name": "parent_job",
|
||||
"ordinal": 1,
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
null,
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT flow_status, parent_job FROM completed_job WHERE id = $1 AND workspace_id = $2 \n UNION ALL SELECT flow_status, parent_job FROM queue WHERE id = $1 AND workspace_id = $2 "
|
||||
},
|
||||
"930ad84a4db26fa6d2c8447d447099e944a0ea7b1266b4d02cee620fe3d761a5": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
|
||||
use git_version::git_version;
|
||||
use sqlx::{Pool, Postgres};
|
||||
@@ -15,6 +15,7 @@ use windmill_common::utils::rd_string;
|
||||
const GIT_VERSION: &str = git_version!(args = ["--tag", "--always"], fallback = "unknown-version");
|
||||
const DEFAULT_NUM_WORKERS: usize = 3;
|
||||
const DEFAULT_PORT: u16 = 8000;
|
||||
const DEFAULT_SERVER_BIND_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
|
||||
|
||||
mod ee;
|
||||
|
||||
@@ -39,6 +40,11 @@ async fn main() -> anyhow::Result<()> {
|
||||
.transpose()?
|
||||
.flatten();
|
||||
|
||||
let server_bind_address: IpAddr = std::env::var("SERVER_BIND_ADDR")
|
||||
.ok()
|
||||
.and_then(|x| x.parse().ok() )
|
||||
.unwrap_or(IpAddr::from(DEFAULT_SERVER_BIND_ADDR));
|
||||
|
||||
let port: u16 = std::env::var("PORT")
|
||||
.ok()
|
||||
.and_then(|x| x.parse::<u16>().ok())
|
||||
@@ -60,8 +66,63 @@ async fn main() -> anyhow::Result<()> {
|
||||
let (tx, rx) = tokio::sync::broadcast::channel::<()>(3);
|
||||
let shutdown_signal = windmill_common::shutdown_signal(tx);
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
tracing::info!(
|
||||
"
|
||||
##############################
|
||||
Windmill Enterprise Edition {GIT_VERSION}
|
||||
##############################"
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
tracing::info!(
|
||||
"
|
||||
##############################
|
||||
Windmill Community Edition {GIT_VERSION}
|
||||
##############################"
|
||||
);
|
||||
|
||||
display_config(vec![
|
||||
"DISABLE_NSJAIL",
|
||||
"DISABLE_SERVER",
|
||||
"NUM_WORKERS",
|
||||
"METRICS_ADDR",
|
||||
"JSON_FMT",
|
||||
"BASE_URL",
|
||||
"BASE_INTERNAL_URL",
|
||||
"TIMEOUT",
|
||||
"SLEEP_QUEUE",
|
||||
"MAX_LOG_SIZE",
|
||||
"SERVER_BIND_ADDR",
|
||||
"PORT",
|
||||
"KEEP_JOB_DIR",
|
||||
"S3_CACHE_BUCKET",
|
||||
"TAR_CACHE_RATE",
|
||||
"COOKIE_DOMAIN",
|
||||
"PYTHON_PATH",
|
||||
"DENO_PATH",
|
||||
"GO_PATH",
|
||||
"PIP_INDEX_URL",
|
||||
"PIP_EXTRA_INDEX_URL",
|
||||
"PIP_TRUSTED_HOST",
|
||||
"PATH",
|
||||
"HOME",
|
||||
"DATABASE_CONNECTIONS",
|
||||
"TIMEOUT_WAIT_RESULT",
|
||||
"QUEUE_LIMIT_WAIT_RESULT",
|
||||
"DENO_AUTH_TOKENS",
|
||||
"DENO_FLAGS",
|
||||
"PIP_LOCAL_DEPENDENCIES",
|
||||
"ADDITIONAL_PYTHON_PATHS",
|
||||
"INCLUDE_HEADERS",
|
||||
"WHITELIST_WORKSPACES",
|
||||
"BLACKLIST_WORKSPACES",
|
||||
"NEW_USER_WEBHOOK",
|
||||
"CLOUD_HOSTED",
|
||||
]);
|
||||
|
||||
if server_mode || num_workers > 0 {
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||
let addr = SocketAddr::from((server_bind_address, port));
|
||||
|
||||
let server_f = async {
|
||||
if server_mode {
|
||||
@@ -72,60 +133,6 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let workers_f = async {
|
||||
if num_workers > 0 {
|
||||
#[cfg(feature = "enterprise")]
|
||||
tracing::info!(
|
||||
"
|
||||
##############################
|
||||
Windmill Enterprise Edition {GIT_VERSION}
|
||||
##############################"
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
tracing::info!(
|
||||
"
|
||||
##############################
|
||||
Windmill Community Edition {GIT_VERSION}
|
||||
##############################"
|
||||
);
|
||||
|
||||
display_config(vec![
|
||||
"DISABLE_NSJAIL",
|
||||
"DISABLE_SERVER",
|
||||
"NUM_WORKERS",
|
||||
"METRICS_ADDR",
|
||||
"JSON_FMT",
|
||||
"BASE_URL",
|
||||
"BASE_INTERNAL_URL",
|
||||
"TIMEOUT",
|
||||
"SLEEP_QUEUE",
|
||||
"MAX_LOG_SIZE",
|
||||
"PORT",
|
||||
"KEEP_JOB_DIR",
|
||||
"S3_CACHE_BUCKET",
|
||||
"TAR_CACHE_RATE",
|
||||
"COOKIE_DOMAIN",
|
||||
"PYTHON_PATH",
|
||||
"DENO_PATH",
|
||||
"GO_PATH",
|
||||
"PIP_INDEX_URL",
|
||||
"PIP_EXTRA_INDEX_URL",
|
||||
"PIP_TRUSTED_HOST",
|
||||
"PATH",
|
||||
"HOME",
|
||||
"DATABASE_CONNECTIONS",
|
||||
"TIMEOUT_WAIT_RESULT",
|
||||
"QUEUE_LIMIT_WAIT_RESULT",
|
||||
"DENO_AUTH_TOKENS",
|
||||
"DENO_FLAGS",
|
||||
"PIP_LOCAL_DEPENDENCIES",
|
||||
"ADDITIONAL_PYTHON_PATHS",
|
||||
"INCLUDE_HEADERS",
|
||||
"WHITELIST_WORKSPACES",
|
||||
"BLACKLIST_WORKSPACES",
|
||||
"NEW_USER_WEBHOOK",
|
||||
"CLOUD_HOSTED",
|
||||
]);
|
||||
|
||||
run_workers(
|
||||
db.clone(),
|
||||
rx.resubscribe(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.70.1
|
||||
version: 1.72.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -3337,6 +3337,22 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CompletedJob"
|
||||
|
||||
/w/{workspace}/jobs/completed/get_result/{id}:
|
||||
get:
|
||||
summary: get completed job result
|
||||
operationId: getCompletedJobResult
|
||||
tags:
|
||||
- job
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/JobId"
|
||||
responses:
|
||||
"200":
|
||||
description: result
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
|
||||
/w/{workspace}/jobs/completed/delete/{id}:
|
||||
post:
|
||||
summary: delete completed job (erase content but keep run id)
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
*/
|
||||
|
||||
use axum::{
|
||||
extract::{Extension, Path},
|
||||
extract::{Extension, Path, Query},
|
||||
routing::{get, post, put},
|
||||
Json, Router,
|
||||
};
|
||||
use hyper::StatusCode;
|
||||
use hyper::{HeaderMap, StatusCode};
|
||||
use serde::Deserialize;
|
||||
use windmill_common::{
|
||||
error::{JsonResult, Result},
|
||||
utils::{not_found_if_none, StripPath},
|
||||
@@ -19,6 +20,7 @@ use windmill_common::{
|
||||
|
||||
use crate::{
|
||||
db::{UserDB, DB},
|
||||
jobs::add_include_headers,
|
||||
users::Authed,
|
||||
};
|
||||
|
||||
@@ -83,13 +85,21 @@ pub async fn new_payload(
|
||||
Ok(StatusCode::CREATED)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct IncludeHeaderQuery {
|
||||
include_header: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn update_payload(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(payload): Json<serde_json::Value>,
|
||||
Query(run_query): Query<IncludeHeaderQuery>,
|
||||
headers: HeaderMap,
|
||||
Json(args): Json<Option<serde_json::Map<String, serde_json::Value>>>,
|
||||
) -> Result<StatusCode> {
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
let args = add_include_headers(&run_query.include_header, headers, args.unwrap_or_default());
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE capture
|
||||
@@ -99,7 +109,7 @@ pub async fn update_payload(
|
||||
",
|
||||
&w_id,
|
||||
&path.to_path(),
|
||||
&payload,
|
||||
serde_json::json!(args),
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
|
||||
@@ -295,28 +295,38 @@ impl RunJobQuery {
|
||||
fn add_include_headers(
|
||||
&self,
|
||||
headers: HeaderMap,
|
||||
mut args: serde_json::Map<String, serde_json::Value>,
|
||||
args: serde_json::Map<String, serde_json::Value>,
|
||||
) -> serde_json::Map<String, serde_json::Value> {
|
||||
let whitelist = self
|
||||
.include_header
|
||||
.as_ref()
|
||||
.map(|s| s.split(",").map(|s| s.to_string()).collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
whitelist
|
||||
.iter()
|
||||
.chain(INCLUDE_HEADERS.iter())
|
||||
.for_each(|h| {
|
||||
if let Some(v) = headers.get(h) {
|
||||
args.insert(
|
||||
h.to_string().to_lowercase().replace('-', "_"),
|
||||
serde_json::Value::String(v.to_str().unwrap().to_string()),
|
||||
);
|
||||
}
|
||||
});
|
||||
args
|
||||
return add_include_headers(&self.include_header, headers, args);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_include_headers(
|
||||
include_header: &Option<String>,
|
||||
headers: HeaderMap,
|
||||
mut args: serde_json::Map<String, serde_json::Value>,
|
||||
) -> serde_json::Map<String, serde_json::Value> {
|
||||
if include_header.is_none() {
|
||||
return args;
|
||||
}
|
||||
let whitelist = include_header
|
||||
.as_ref()
|
||||
.map(|s| s.split(",").map(|s| s.to_string()).collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
|
||||
whitelist
|
||||
.iter()
|
||||
.chain(INCLUDE_HEADERS.iter())
|
||||
.for_each(|h| {
|
||||
if let Some(v) = headers.get(h) {
|
||||
args.insert(
|
||||
h.to_string().to_lowercase().replace('-', "_"),
|
||||
serde_json::Value::String(v.to_str().unwrap().to_string()),
|
||||
);
|
||||
}
|
||||
});
|
||||
args
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct ListQueueQuery {
|
||||
pub script_path_start: Option<String>,
|
||||
@@ -1775,6 +1785,7 @@ async fn get_completed_job(
|
||||
.fetch_optional(&db)
|
||||
.await?;
|
||||
|
||||
tracing::info!("job_o: {:?}", job_o);
|
||||
let job = not_found_if_none(job_o, "Completed Job", id.to_string())?;
|
||||
Ok(Json(job))
|
||||
}
|
||||
|
||||
@@ -1311,7 +1311,9 @@ async fn create_user(
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = HTTP_CLIENT
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &nu.email, "name": &nu.name, "event": "new_user"}))
|
||||
.json(
|
||||
&serde_json::json!({"email" : &nu.email, "name": &nu.name, "event": "global_add"}),
|
||||
)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
|
||||
@@ -965,7 +965,7 @@ async fn invite_user(
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = &HTTP_CLIENT
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &nu.email, "event": "new_invite"}))
|
||||
.json(&serde_json::json!({"email" : &nu.email, "event": "workspace_invite"}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
@@ -1002,6 +1002,15 @@ async fn add_user(
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = HTTP_CLIENT
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &nu.email, "event": "workspace_add"}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
}
|
||||
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
format!("user with email {} added", nu.email),
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use reqwest::Client;
|
||||
@@ -17,7 +20,7 @@ use ulid::Ulid;
|
||||
use uuid::Uuid;
|
||||
use windmill_audit::{audit_log, ActionKind};
|
||||
use windmill_common::{
|
||||
error::{self, Error},
|
||||
error::{self, to_anyhow, Error},
|
||||
flow_status::{FlowStatus, JobResult, MAX_RETRY_ATTEMPTS, MAX_RETRY_INTERVAL},
|
||||
flows::{FlowModule, FlowModuleValue, FlowValue},
|
||||
scripts::{get_full_hub_script_by_path, HubScript, ScriptHash, ScriptLang},
|
||||
@@ -150,6 +153,41 @@ pub async fn pull(
|
||||
Ok(job)
|
||||
}
|
||||
|
||||
pub async fn find_recursively_downward(
|
||||
db: &Pool<Postgres>,
|
||||
w_id: &str,
|
||||
flow_id: Uuid,
|
||||
node_id: &str,
|
||||
) -> windmill_common::error::Result<Option<JobResult>> {
|
||||
let mut bfs_stack = VecDeque::new();
|
||||
bfs_stack.push_back(flow_id);
|
||||
while bfs_stack.len() > 0 {
|
||||
let parent_id = bfs_stack.pop_front().unwrap();
|
||||
let job = sqlx::query_scalar!(
|
||||
"SELECT flow_status FROM completed_job WHERE id = $1 AND workspace_id = $2
|
||||
UNION ALL SELECT flow_status FROM queue WHERE id = $1 AND workspace_id = $2 ",
|
||||
parent_id,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
.flatten();
|
||||
if let Some(r) = job {
|
||||
let status = serde_json::from_value::<FlowStatus>(r).map_err(to_anyhow)?;
|
||||
for m in status.modules.iter() {
|
||||
let id = m.id();
|
||||
if id == node_id {
|
||||
return Ok(m.job_result());
|
||||
}
|
||||
if let Some(job_id) = m.job() {
|
||||
bfs_stack.push_back(job_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn get_result_by_id(
|
||||
db: Pool<Postgres>,
|
||||
mut skip_direct: bool,
|
||||
@@ -159,10 +197,12 @@ pub async fn get_result_by_id(
|
||||
) -> error::Result<serde_json::Value> {
|
||||
let mut result_id: Option<JobResult> = None;
|
||||
let mut parent_id = Uuid::from_str(&flow_id).ok();
|
||||
let mut lparent_id = parent_id.clone();
|
||||
while result_id.is_none() && parent_id.is_some() {
|
||||
if !skip_direct {
|
||||
let r = sqlx::query!(
|
||||
"SELECT flow_status, parent_job FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT flow_status, parent_job FROM queue WHERE id = $1 AND workspace_id = $2 ",
|
||||
"SELECT flow_status, parent_job FROM completed_job WHERE id = $1 AND workspace_id = $2
|
||||
UNION ALL SELECT flow_status, parent_job FROM queue WHERE id = $1 AND workspace_id = $2 ",
|
||||
parent_id.unwrap(),
|
||||
w_id,
|
||||
)
|
||||
@@ -174,6 +214,7 @@ pub async fn get_result_by_id(
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::InternalErr(format!("requiring a flow status value")))?
|
||||
.to_owned();
|
||||
lparent_id = parent_id;
|
||||
parent_id = r.parent_job;
|
||||
let status_o = serde_json::from_value::<FlowStatus>(value).ok();
|
||||
result_id = status_o.and_then(|status| {
|
||||
@@ -195,10 +236,17 @@ pub async fn get_result_by_id(
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.flatten();
|
||||
lparent_id = parent_id;
|
||||
parent_id = q_parent;
|
||||
skip_direct = false
|
||||
}
|
||||
}
|
||||
// we could not find the node going upward from the flow by looking at all the jobs (in progress or completed)
|
||||
// we now look downward from the flow root to the all the children completed job for a job that might hide itself
|
||||
// in a deep non-direct parent job such as in nested branches
|
||||
if result_id.is_none() && lparent_id.is_some() {
|
||||
result_id = find_recursively_downward(&db, &w_id, lparent_id.unwrap(), &node_id).await?;
|
||||
}
|
||||
let result_id = windmill_common::utils::not_found_if_none(
|
||||
result_id,
|
||||
"Flow result by id",
|
||||
|
||||
@@ -145,7 +145,7 @@ fn add_closing_bracket(s: &str) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
const SPLIT_PAT: &str = ";\n";
|
||||
const SPLIT_PAT: &str = ";";
|
||||
async fn eval(
|
||||
context: &mut JsRuntime,
|
||||
expr: &str,
|
||||
@@ -154,14 +154,21 @@ async fn eval(
|
||||
by_id: Option<IdContext>,
|
||||
base_internal_url: &str,
|
||||
) -> anyhow::Result<serde_json::Value> {
|
||||
let expr = expr.trim();
|
||||
let expr = format!(
|
||||
"{}\nreturn {};",
|
||||
expr.split(SPLIT_PAT)
|
||||
.take(expr.split(SPLIT_PAT).count() - 1)
|
||||
.join("\n"),
|
||||
expr.split(SPLIT_PAT).last().unwrap_or_else(|| "")
|
||||
);
|
||||
let exprs = expr
|
||||
.trim()
|
||||
.split(SPLIT_PAT)
|
||||
.map(|x| x.trim())
|
||||
.filter(|x| !x.is_empty())
|
||||
.collect::<Vec<&str>>();
|
||||
let expr = if exprs.is_empty() {
|
||||
"return undefined;".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"{};\n return {};",
|
||||
exprs.iter().take(exprs.len() - 1).join(";\n"),
|
||||
exprs.last().unwrap()
|
||||
)
|
||||
};
|
||||
let (api_code, by_id_code) = if let Some(EvalCreds { workspace, token }) = creds {
|
||||
let by_id_code = if let Some(by_id) = by_id {
|
||||
format!(
|
||||
@@ -198,12 +205,12 @@ const results = new Proxy({{}}, {{
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
let v_str = match v {
|
||||
JobResult::SingleJob(x) => x.to_string(),
|
||||
JobResult::SingleJob(x) => format!("\"{x}\""),
|
||||
JobResult::ListJob(x) => {
|
||||
format!("[{}]", x.iter().map(|x| x.to_string()).join(","))
|
||||
format!("[{}]", x.iter().map(|x| format!("\"{x}\"")).join(","))
|
||||
}
|
||||
};
|
||||
format!("\"{k}\": \"{v_str}\"")
|
||||
format!("\"{k}\": {v_str}")
|
||||
})
|
||||
.join(","),
|
||||
by_id.previous_id,
|
||||
@@ -292,9 +299,8 @@ async fn op_get_result(args: Vec<String>) -> Result<serde_json::Value, anyhow::E
|
||||
let base_url = &args[3];
|
||||
let client = windmill_api_client::create_client(base_url, token.clone());
|
||||
let result = client
|
||||
.get_completed_job(workspace, &id.parse()?)
|
||||
.get_completed_job_result(workspace, &id.parse()?)
|
||||
.await?
|
||||
.result
|
||||
.clone();
|
||||
Ok(serde_json::json!(result))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ async function tryResolveWorkspace(
|
||||
{ isError: false; value: Workspace } | { isError: true; error: string }
|
||||
> {
|
||||
const cache = (opts as any).__secret_workspace;
|
||||
if (cache) return cache;
|
||||
if (cache) return { isError: false, value: cache };
|
||||
|
||||
if (opts.workspace) {
|
||||
const e = await getWorkspaceByName(opts.workspace);
|
||||
@@ -53,7 +53,6 @@ export async function resolveWorkspace(
|
||||
): Promise<Workspace> {
|
||||
const res = await tryResolveWorkspace(opts);
|
||||
if (res.isError) {
|
||||
console.log(res.error);
|
||||
return Deno.exit(-1);
|
||||
} else {
|
||||
return res.value;
|
||||
@@ -62,8 +61,8 @@ export async function resolveWorkspace(
|
||||
|
||||
export async function requireLogin(opts: GlobalOptions): Promise<GlobalUserInfo> {
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
|
||||
let token = await tryGetLoginInfo(opts);
|
||||
|
||||
if (!token) {
|
||||
token = workspace.token;
|
||||
}
|
||||
@@ -80,9 +79,9 @@ export async function requireLogin(opts: GlobalOptions): Promise<GlobalUserInfo>
|
||||
if (!newToken) {
|
||||
throw new Error("Could not reauth");
|
||||
}
|
||||
removeWorkspace(workspace.name);
|
||||
removeWorkspace(workspace.name, false, opts);
|
||||
workspace.token = newToken;
|
||||
addWorkspace(workspace);
|
||||
addWorkspace(workspace, opts);
|
||||
|
||||
setClient(
|
||||
token,
|
||||
|
||||
10
cli/flow.ts
10
cli/flow.ts
@@ -192,7 +192,7 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) {
|
||||
}
|
||||
async function run(
|
||||
opts: GlobalOptions & {
|
||||
input: string[];
|
||||
data?: string;
|
||||
silent: boolean;
|
||||
},
|
||||
path: string,
|
||||
@@ -200,7 +200,8 @@ async function run(
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
|
||||
const input = await resolve(opts.input);
|
||||
const input = opts.data ? await resolve(opts.data) : {};
|
||||
|
||||
|
||||
const id = await JobService.runFlowByPath({
|
||||
workspace: workspace.workspaceId,
|
||||
@@ -236,6 +237,7 @@ async function run(
|
||||
|
||||
if (!opts.silent) {
|
||||
console.log(colors.green.underline.bold("Flow ran to completion"));
|
||||
console.log()
|
||||
}
|
||||
const jobInfo = await JobService.getCompletedJob({
|
||||
workspace: workspace.workspaceId,
|
||||
@@ -257,8 +259,8 @@ const command = new Command()
|
||||
.command("run", "run a flow by path.")
|
||||
.arguments("<path:string>")
|
||||
.option(
|
||||
"-i --input [inputs...:string]",
|
||||
"Inputs specified as JSON objects or simply as <name>=<value>. Supports file inputs using @<filename> and stdin using @- these also need to be formatted as JSON. Later inputs override earlier ones.",
|
||||
"-d --data <data:string>",
|
||||
"Inputs specified as a JSON string or a file using @<filename> or stdin using @-.",
|
||||
)
|
||||
.option(
|
||||
"-s --silent",
|
||||
|
||||
@@ -13,7 +13,7 @@ import sync from "./sync.ts";
|
||||
import { tryResolveVersion } from "./context.ts";
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
|
||||
const VERSION = "v1.70.1";
|
||||
const VERSION = "v1.72.0";
|
||||
|
||||
let command: any = new Command()
|
||||
.name("wmill")
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Table,
|
||||
} from "./deps.ts";
|
||||
import { Any, array, decoverto, model, property } from "./decoverto.ts";
|
||||
import { writeAllSync } from "https://deno.land/std@0.176.0/streams/mod.ts";
|
||||
|
||||
@model()
|
||||
export class ScriptFile {
|
||||
@@ -280,50 +281,27 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) {
|
||||
.render();
|
||||
}
|
||||
|
||||
export async function resolve(inputs: string[]): Promise<Record<string, any>> {
|
||||
let result = {};
|
||||
|
||||
if (!inputs) {
|
||||
return result;
|
||||
export async function resolve(input: string): Promise<Record<string, any>> {
|
||||
if (!input) {
|
||||
throw new Error("No data given");
|
||||
}
|
||||
|
||||
for (const input of inputs) {
|
||||
let data: string;
|
||||
if (input.startsWith("@")) {
|
||||
if (input == "@-") {
|
||||
data = new TextDecoder().decode(await readAll(Deno.stdin));
|
||||
} else {
|
||||
data = await Deno.readTextFile(input.substring(1));
|
||||
}
|
||||
} else {
|
||||
if (input.startsWith("{")) {
|
||||
data = input;
|
||||
} else {
|
||||
const key = input.split("=", 1)[0];
|
||||
const value = input.substring(key.length + 1);
|
||||
let o;
|
||||
try {
|
||||
o = JSON.parse(value);
|
||||
} catch {
|
||||
o = value;
|
||||
}
|
||||
data = JSON.stringify(Object.fromEntries([[key, o]]));
|
||||
}
|
||||
}
|
||||
let jsonObj;
|
||||
try {
|
||||
jsonObj = JSON.parse(data);
|
||||
} catch {
|
||||
jsonObj = data;
|
||||
}
|
||||
result = { ...result, ...jsonObj };
|
||||
if (input == "@-") {
|
||||
input = new TextDecoder().decode(await readAll(Deno.stdin));
|
||||
} if (input[0] == "@") {
|
||||
input = await Deno.readTextFile(input.substring(1));
|
||||
}
|
||||
try {
|
||||
return JSON.parse(input);
|
||||
} catch (e) {
|
||||
console.error("Impossible to parse input as JSON", input)
|
||||
throw e
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function run(
|
||||
opts: GlobalOptions & {
|
||||
input: string[];
|
||||
data?: string;
|
||||
silent: boolean;
|
||||
},
|
||||
path: string,
|
||||
@@ -331,7 +309,8 @@ async function run(
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
|
||||
const input = await resolve(opts.input);
|
||||
|
||||
const input = opts.data ? await resolve(opts.data) : {};
|
||||
const id = await JobService.runScriptByPath({
|
||||
workspace: workspace.workspaceId,
|
||||
path,
|
||||
@@ -364,7 +343,9 @@ export async function track_job(workspace: string, id: string) {
|
||||
const result = await JobService.getCompletedJob({ workspace, id });
|
||||
|
||||
console.log(result.logs);
|
||||
console.log()
|
||||
console.log(colors.bold.underline.green("Job Completed"));
|
||||
console.log()
|
||||
return;
|
||||
} catch {
|
||||
/* ignore */
|
||||
@@ -403,7 +384,7 @@ export async function track_job(workspace: string, id: string) {
|
||||
}
|
||||
|
||||
if (updates.new_logs) {
|
||||
console.log(updates.new_logs);
|
||||
writeAllSync(Deno.stdout, new TextEncoder().encode(updates.new_logs));
|
||||
logOffset += updates.new_logs.length;
|
||||
}
|
||||
|
||||
@@ -426,12 +407,15 @@ export async function track_job(workspace: string, id: string) {
|
||||
if ((final_job.logs?.length ?? -1) > logOffset) {
|
||||
console.log(final_job.logs!.substring(logOffset));
|
||||
}
|
||||
|
||||
console.log("\n")
|
||||
if (final_job.success) {
|
||||
console.log(colors.bold.underline.green("Job Completed"));
|
||||
|
||||
} else {
|
||||
console.log(colors.bold.underline.red("Job Completed"));
|
||||
}
|
||||
console.log()
|
||||
|
||||
} catch {
|
||||
console.log("Job appears to have completed, but no data can be retrieved");
|
||||
}
|
||||
@@ -466,8 +450,8 @@ const command = new Command()
|
||||
.command("run", "run a script by path")
|
||||
.arguments("<path:string>")
|
||||
.option(
|
||||
"-i --input [inputs...:string]",
|
||||
"Inputs specified as JSON objects or simply as <name>=<value>. Supports file inputs using @<filename> and stdin using @- these also need to be formatted as JSON. Later inputs override earlier ones.",
|
||||
"-d --data <data:string>",
|
||||
"Inputs specified as a JSON string or a file using @<filename> or stdin using @-.",
|
||||
)
|
||||
.option(
|
||||
"-s --silent",
|
||||
|
||||
@@ -9,9 +9,11 @@ import {
|
||||
Input,
|
||||
setClient,
|
||||
Table,
|
||||
UserService,
|
||||
WorkspaceService,
|
||||
} from "./deps.ts";
|
||||
import { decoverto, model, property } from "./decoverto.ts";
|
||||
import { requireLogin } from "./context.ts";
|
||||
|
||||
@model()
|
||||
export class Workspace {
|
||||
@@ -308,6 +310,11 @@ async function remove(_opts: GlobalOptions, name: string) {
|
||||
await removeWorkspace(name, false, _opts);
|
||||
}
|
||||
|
||||
async function whoami(_opts: GlobalOptions) {
|
||||
await requireLogin(_opts)
|
||||
console.log(await UserService.globalWhoami())
|
||||
}
|
||||
|
||||
const command = new Command()
|
||||
.description("workspace related commands")
|
||||
.action(list as any)
|
||||
@@ -335,6 +342,9 @@ const command = new Command()
|
||||
.command("remove")
|
||||
.description("Remove a workspace")
|
||||
.arguments("<workspace_name:string>")
|
||||
.action(remove as any);
|
||||
.action(remove as any)
|
||||
.command("whoami")
|
||||
.description("Show the currently active user")
|
||||
.action(whoami as any);
|
||||
|
||||
export default command;
|
||||
|
||||
493
frontend/package-lock.json
generated
493
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.70.1",
|
||||
"version": "1.72.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill",
|
||||
"version": "1.70.1",
|
||||
"version": "1.72.0",
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
@@ -45,7 +45,7 @@
|
||||
"@types/vscode": "~1.74.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@windmill-labs/svelte-grid": "^5.1.4",
|
||||
"@windmill-labs/svelte-grid": "^5.1.6",
|
||||
"@windmill-labs/svelvet": "^4.0.20",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@@ -54,6 +54,7 @@
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"ol": "^7.2.2",
|
||||
"openapi-typescript-codegen": "^0.23.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"postcss": "^8.4.18",
|
||||
@@ -718,6 +719,49 @@
|
||||
"resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-0.9.1.tgz",
|
||||
"integrity": "sha512-xlL3HMhHdZX/UVTyjiLB0hpUa71HERhegvrfkUD9NxGnYh9kk9+/llynEWS+NmGRB2KlRKoY+HmWZzt/c0whJQ=="
|
||||
},
|
||||
"node_modules/@mapbox/jsonlint-lines-primitives": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
|
||||
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/mapbox-gl-style-spec": {
|
||||
"version": "13.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.28.0.tgz",
|
||||
"integrity": "sha512-B8xM7Fp1nh5kejfIl4SWeY0gtIeewbuRencqO3cJDrCHZpaPg7uY+V8abuR+esMeuOjRl5cLhVTP40v+1ywxbg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
||||
"@mapbox/point-geometry": "^0.1.0",
|
||||
"@mapbox/unitbezier": "^0.0.0",
|
||||
"csscolorparser": "~1.0.2",
|
||||
"json-stringify-pretty-compact": "^2.0.0",
|
||||
"minimist": "^1.2.6",
|
||||
"rw": "^1.3.3",
|
||||
"sort-object": "^0.3.2"
|
||||
},
|
||||
"bin": {
|
||||
"gl-style-composite": "bin/gl-style-composite.js",
|
||||
"gl-style-format": "bin/gl-style-format.js",
|
||||
"gl-style-migrate": "bin/gl-style-migrate.js",
|
||||
"gl-style-validate": "bin/gl-style-validate.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/point-geometry": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
|
||||
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@mapbox/unitbezier": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz",
|
||||
"integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -753,6 +797,12 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@petamoriken/float16": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.7.1.tgz",
|
||||
"integrity": "sha512-oXZOc+aePd0FnhTWk15pyqK+Do87n0TyLV1nxdEougE95X/WXWDqmQobfhgnSY7QsWn5euZUWuDVeTQvoQ5VNw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz",
|
||||
@@ -1268,9 +1318,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@windmill-labs/svelte-grid": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.4.tgz",
|
||||
"integrity": "sha512-alxQ8su/osmHodl1JMta+0dvlc2qnYwiXsoeC8Ys8hrLywD03y5tjxYV0L0WXvOnMKG1S9GbqGuDVCk+UmVuKg==",
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.6.tgz",
|
||||
"integrity": "sha512-wXHIG7XeMxwxnfaHajhjvz9c0u5uGgiQtTk/QxAVyViGdshsclfIfIWZF113QQBKCKp2jY/zP1cj64BrqioRoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@windmill-labs/svelvet": {
|
||||
@@ -2173,6 +2223,12 @@
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/csscolorparser": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz",
|
||||
"integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
@@ -2618,6 +2674,12 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/earcut": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
|
||||
"integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@@ -3181,6 +3243,36 @@
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/geotiff": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.7.tgz",
|
||||
"integrity": "sha512-FKvFTNowMU5K6lHYY2f83d4lS2rsCNdpUC28AX61x9ZzzqPNaWFElWv93xj0eJFaNyOYA63ic5OzJ88dHpoA5Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@petamoriken/float16": "^3.4.7",
|
||||
"lerc": "^3.0.0",
|
||||
"pako": "^2.0.4",
|
||||
"parse-headers": "^2.0.2",
|
||||
"quick-lru": "^6.1.1",
|
||||
"web-worker": "^1.2.0",
|
||||
"xml-utils": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.19"
|
||||
}
|
||||
},
|
||||
"node_modules/geotiff/node_modules/quick-lru": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz",
|
||||
"integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
@@ -3442,6 +3534,26 @@
|
||||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
@@ -3749,6 +3861,12 @@
|
||||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-stringify-pretty-compact": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz",
|
||||
"integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jsonc-parser": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
|
||||
@@ -3792,6 +3910,12 @@
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lerc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz",
|
||||
"integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||
@@ -3926,6 +4050,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mapbox-to-css-font": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/mapbox-to-css-font/-/mapbox-to-css-font-2.4.2.tgz",
|
||||
"integrity": "sha512-f+NBjJJY4T3dHtlEz1wCG7YFlkODEjFIYlxDdLIDMNpkSksqTt+l/d4rjuwItxuzkuMFvPyrjzV2lxRM4ePcIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mathml-tag-names": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
|
||||
@@ -4365,6 +4495,33 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/ol": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ol/-/ol-7.2.2.tgz",
|
||||
"integrity": "sha512-eqJ1hhVQQ3Ap4OhYq9DRu5pz9RMpLhmoTauDoIqpn7logVi1AJE+lXjEHrPrTSuZYjtFbMgqr07sxoLNR65nrw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"earcut": "^2.2.3",
|
||||
"geotiff": "^2.0.7",
|
||||
"ol-mapbox-style": "^9.2.0",
|
||||
"pbf": "3.2.1",
|
||||
"rbush": "^3.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/openlayers"
|
||||
}
|
||||
},
|
||||
"node_modules/ol-mapbox-style": {
|
||||
"version": "9.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-9.7.0.tgz",
|
||||
"integrity": "sha512-YX3u8FBJHsRHaoGxmd724Mp5WPTuV7wLQW6zZhcihMuInsSdCX1EiZfU+8IAL7jG0pbgl5YgC0aWE/MXJcUXxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@mapbox/mapbox-gl-style-spec": "^13.23.1",
|
||||
"mapbox-to-css-font": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
@@ -4473,6 +4630,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -4485,6 +4648,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-headers": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz",
|
||||
"integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
@@ -4575,6 +4744,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pbf": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
|
||||
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ieee754": "^1.1.12",
|
||||
"resolve-protobuf-schema": "^2.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"pbf": "bin/pbf"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
@@ -5219,6 +5401,12 @@
|
||||
"svelte": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/protocol-buffers-schema": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
|
||||
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
@@ -5267,6 +5455,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/quickselect": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
|
||||
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
|
||||
@@ -5277,6 +5471,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/rbush": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
|
||||
"integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"quickselect": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
@@ -5549,6 +5752,15 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-protobuf-schema": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
|
||||
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"protocol-buffers-schema": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
@@ -5613,6 +5825,12 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sade": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
||||
@@ -5868,6 +6086,37 @@
|
||||
"sorcery": "bin/sorcery"
|
||||
}
|
||||
},
|
||||
"node_modules/sort-asc": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz",
|
||||
"integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sort-desc": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz",
|
||||
"integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sort-object": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz",
|
||||
"integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"sort-asc": "^0.1.0",
|
||||
"sort-desc": "^0.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -6937,6 +7186,12 @@
|
||||
"npm": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/web-worker": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz",
|
||||
"integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -7142,6 +7397,12 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-utils": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.3.0.tgz",
|
||||
"integrity": "sha512-i4PIrX33Wd66dvwo4syicwlwmnr6wuvvn4f2ku9hA67C2Uk62Xubczuhct+Evnd12/DV71qKNeDdJwES8HX1RA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@@ -7570,6 +7831,40 @@
|
||||
"resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-0.9.1.tgz",
|
||||
"integrity": "sha512-xlL3HMhHdZX/UVTyjiLB0hpUa71HERhegvrfkUD9NxGnYh9kk9+/llynEWS+NmGRB2KlRKoY+HmWZzt/c0whJQ=="
|
||||
},
|
||||
"@mapbox/jsonlint-lines-primitives": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
|
||||
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@mapbox/mapbox-gl-style-spec": {
|
||||
"version": "13.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.28.0.tgz",
|
||||
"integrity": "sha512-B8xM7Fp1nh5kejfIl4SWeY0gtIeewbuRencqO3cJDrCHZpaPg7uY+V8abuR+esMeuOjRl5cLhVTP40v+1ywxbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
||||
"@mapbox/point-geometry": "^0.1.0",
|
||||
"@mapbox/unitbezier": "^0.0.0",
|
||||
"csscolorparser": "~1.0.2",
|
||||
"json-stringify-pretty-compact": "^2.0.0",
|
||||
"minimist": "^1.2.6",
|
||||
"rw": "^1.3.3",
|
||||
"sort-object": "^0.3.2"
|
||||
}
|
||||
},
|
||||
"@mapbox/point-geometry": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
|
||||
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@mapbox/unitbezier": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz",
|
||||
"integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==",
|
||||
"dev": true
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -7596,6 +7891,12 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@petamoriken/float16": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.7.1.tgz",
|
||||
"integrity": "sha512-oXZOc+aePd0FnhTWk15pyqK+Do87n0TyLV1nxdEougE95X/WXWDqmQobfhgnSY7QsWn5euZUWuDVeTQvoQ5VNw==",
|
||||
"dev": true
|
||||
},
|
||||
"@playwright/test": {
|
||||
"version": "1.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz",
|
||||
@@ -7924,9 +8225,9 @@
|
||||
}
|
||||
},
|
||||
"@windmill-labs/svelte-grid": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.4.tgz",
|
||||
"integrity": "sha512-alxQ8su/osmHodl1JMta+0dvlc2qnYwiXsoeC8Ys8hrLywD03y5tjxYV0L0WXvOnMKG1S9GbqGuDVCk+UmVuKg==",
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.6.tgz",
|
||||
"integrity": "sha512-wXHIG7XeMxwxnfaHajhjvz9c0u5uGgiQtTk/QxAVyViGdshsclfIfIWZF113QQBKCKp2jY/zP1cj64BrqioRoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@windmill-labs/svelvet": {
|
||||
@@ -8567,6 +8868,12 @@
|
||||
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
||||
"dev": true
|
||||
},
|
||||
"csscolorparser": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz",
|
||||
"integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==",
|
||||
"dev": true
|
||||
},
|
||||
"cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
@@ -8895,6 +9202,12 @@
|
||||
"domhandler": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"earcut": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
|
||||
"integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==",
|
||||
"dev": true
|
||||
},
|
||||
"eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@@ -9337,6 +9650,29 @@
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"geotiff": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.7.tgz",
|
||||
"integrity": "sha512-FKvFTNowMU5K6lHYY2f83d4lS2rsCNdpUC28AX61x9ZzzqPNaWFElWv93xj0eJFaNyOYA63ic5OzJ88dHpoA5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@petamoriken/float16": "^3.4.7",
|
||||
"lerc": "^3.0.0",
|
||||
"pako": "^2.0.4",
|
||||
"parse-headers": "^2.0.2",
|
||||
"quick-lru": "^6.1.1",
|
||||
"web-worker": "^1.2.0",
|
||||
"xml-utils": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"quick-lru": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz",
|
||||
"integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
@@ -9529,6 +9865,12 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
@@ -9758,6 +10100,12 @@
|
||||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"json-stringify-pretty-compact": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz",
|
||||
"integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==",
|
||||
"dev": true
|
||||
},
|
||||
"jsonc-parser": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
|
||||
@@ -9793,6 +10141,12 @@
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"lerc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz",
|
||||
"integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==",
|
||||
"dev": true
|
||||
},
|
||||
"levn": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||
@@ -9901,6 +10255,12 @@
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"mapbox-to-css-font": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/mapbox-to-css-font/-/mapbox-to-css-font-2.4.2.tgz",
|
||||
"integrity": "sha512-f+NBjJJY4T3dHtlEz1wCG7YFlkODEjFIYlxDdLIDMNpkSksqTt+l/d4rjuwItxuzkuMFvPyrjzV2lxRM4ePcIA==",
|
||||
"dev": true
|
||||
},
|
||||
"mathml-tag-names": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
|
||||
@@ -10239,6 +10599,29 @@
|
||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||
"dev": true
|
||||
},
|
||||
"ol": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ol/-/ol-7.2.2.tgz",
|
||||
"integrity": "sha512-eqJ1hhVQQ3Ap4OhYq9DRu5pz9RMpLhmoTauDoIqpn7logVi1AJE+lXjEHrPrTSuZYjtFbMgqr07sxoLNR65nrw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"earcut": "^2.2.3",
|
||||
"geotiff": "^2.0.7",
|
||||
"ol-mapbox-style": "^9.2.0",
|
||||
"pbf": "3.2.1",
|
||||
"rbush": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"ol-mapbox-style": {
|
||||
"version": "9.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-9.7.0.tgz",
|
||||
"integrity": "sha512-YX3u8FBJHsRHaoGxmd724Mp5WPTuV7wLQW6zZhcihMuInsSdCX1EiZfU+8IAL7jG0pbgl5YgC0aWE/MXJcUXxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@mapbox/mapbox-gl-style-spec": "^13.23.1",
|
||||
"mapbox-to-css-font": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
@@ -10317,6 +10700,12 @@
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"pako": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
||||
"dev": true
|
||||
},
|
||||
"parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -10326,6 +10715,12 @@
|
||||
"callsites": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"parse-headers": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz",
|
||||
"integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==",
|
||||
"dev": true
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
@@ -10398,6 +10793,16 @@
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
},
|
||||
"pbf": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
|
||||
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ieee754": "^1.1.12",
|
||||
"resolve-protobuf-schema": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
@@ -10787,6 +11192,12 @@
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"protocol-buffers-schema": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
|
||||
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==",
|
||||
"dev": true
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
@@ -10812,6 +11223,12 @@
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"quickselect": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
|
||||
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
|
||||
"dev": true
|
||||
},
|
||||
"range-parser": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
|
||||
@@ -10819,6 +11236,15 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"rbush": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
|
||||
"integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"quickselect": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
@@ -11031,6 +11457,15 @@
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-protobuf-schema": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
|
||||
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"protocol-buffers-schema": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
@@ -11064,6 +11499,12 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
|
||||
"dev": true
|
||||
},
|
||||
"sade": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
||||
@@ -11270,6 +11711,28 @@
|
||||
"sander": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"sort-asc": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz",
|
||||
"integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==",
|
||||
"dev": true
|
||||
},
|
||||
"sort-desc": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz",
|
||||
"integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==",
|
||||
"dev": true
|
||||
},
|
||||
"sort-object": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz",
|
||||
"integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sort-asc": "^0.1.0",
|
||||
"sort-desc": "^0.1.1"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -12027,6 +12490,12 @@
|
||||
"vscode-jsonrpc": "8.0.2"
|
||||
}
|
||||
},
|
||||
"web-worker": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz",
|
||||
"integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -12170,6 +12639,12 @@
|
||||
"signal-exit": "^3.0.7"
|
||||
}
|
||||
},
|
||||
"xml-utils": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.3.0.tgz",
|
||||
"integrity": "sha512-i4PIrX33Wd66dvwo4syicwlwmnr6wuvvn4f2ku9hA67C2Uk62Xubczuhct+Evnd12/DV71qKNeDdJwES8HX1RA==",
|
||||
"dev": true
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.70.1",
|
||||
"version": "1.72.0",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
@@ -25,7 +25,7 @@
|
||||
"@types/vscode": "~1.74.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@windmill-labs/svelte-grid": "^5.1.4",
|
||||
"@windmill-labs/svelte-grid": "^5.1.6",
|
||||
"@windmill-labs/svelvet": "^4.0.20",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@@ -34,6 +34,7 @@
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"ol": "^7.2.2",
|
||||
"openapi-typescript-codegen": "^0.23.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"postcss": "^8.4.18",
|
||||
|
||||
1
frontend/src/global.d.ts
vendored
1
frontend/src/global.d.ts
vendored
@@ -47,6 +47,7 @@ declare module '@windmill-labs/svelte-grid' {
|
||||
onTopId?: string
|
||||
scroller?: undefined
|
||||
sensor?: number
|
||||
parentWidth?: number
|
||||
}
|
||||
|
||||
export interface Slots<T> {
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
<div class="mb-2 text-gray-500 text-sm bg-gray-50/20">
|
||||
as JSON <input type="checkbox" bind:checked={forceJson} /></div
|
||||
>{/if}{#if typeof result == 'object' && Object.keys(result).length > 0}<div
|
||||
class="mb-2 text-sm text-gray-700 relative"
|
||||
class="mb-2 min-w-[400px] text-sm text-gray-700 relative"
|
||||
>The result keys are: <b>{truncate(Object.keys(result).join(', '), 50)}</b>
|
||||
<div class="text-gray-500 text-sm absolute top-0 right-2">
|
||||
<button on:click={jsonViewer.openDrawer}>Expand JSON</button>
|
||||
|
||||
@@ -113,7 +113,6 @@
|
||||
await createSchedule(flow.path)
|
||||
}
|
||||
}
|
||||
sendUserToast(`Flow saved at ${$flowStore.path}`)
|
||||
goto(`/flows/get/${$flowStore.path}?workspace_id=${$workspaceStore}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,9 @@
|
||||
class="w-full border-r border-b border-t border-gray-400 min-h-[150px] p-2 overflow-auto hidden sm:block"
|
||||
>
|
||||
{#if stepDetail == undefined}
|
||||
<span class="font-black text-lg w-full my-4">
|
||||
<SchemaViewer schema={flow?.schema} />
|
||||
|
||||
<span class="font-black text-lg w-full my-4 mt-14">
|
||||
<span>Click on a step to see its details</span>
|
||||
</span>
|
||||
{:else if stepDetail == 'Input'}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
<div
|
||||
class:border={!noBorder}
|
||||
class="grid {!col ? 'grid-cols-2' : 'grid-rows-2'} shadow border-gray-400 h-full"
|
||||
class="grid {!col ? 'grid-cols-2' : 'grid-rows-2'} shadow border-gray-400 h-full max-h-screen"
|
||||
>
|
||||
<div class="bg-white max-h-80 h-full p-1 overflow-auto relative">
|
||||
<div class="bg-white {col ? '' : 'max-h-80'} h-full p-1 overflow-auto relative">
|
||||
<span class="text-gray-500">Result</span>
|
||||
{#if result}
|
||||
<DisplayResult {result} />
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="text-gray-400">No result (result is undefined)</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="overflow-auto max-h-80 h-full relative">
|
||||
<div class="overflow-auto {col ? '' : 'max-h-80'} h-full relative">
|
||||
<LogViewer content={logs ?? ''} isLoading={false} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
export let jobId: string | undefined = undefined
|
||||
export let job: Job | undefined = undefined
|
||||
let isValid: boolean = true
|
||||
let isRunning: boolean = false
|
||||
let jobProgressReset: () => void
|
||||
|
||||
@@ -105,7 +104,6 @@
|
||||
|
||||
{#if isRunning}
|
||||
<Button
|
||||
disabled={!isValid}
|
||||
color="red"
|
||||
on:click={async () => {
|
||||
isRunning = false
|
||||
@@ -132,7 +130,6 @@
|
||||
color="blue"
|
||||
size="sm"
|
||||
btnClasses="w-full max-w-lg"
|
||||
disabled={!isValid}
|
||||
on:click={() => runPreview($previewArgs)}
|
||||
>
|
||||
Test flow <Kbd class="ml-2">Ctrl+Enter</Kbd>
|
||||
@@ -156,7 +153,6 @@
|
||||
compact
|
||||
class="py-4 max-w-3xl"
|
||||
schema={$flowStore.schema}
|
||||
bind:isValid
|
||||
bind:args={$previewArgs}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -505,6 +505,7 @@
|
||||
<div class="px-1 border-b border-black">
|
||||
<JobArgs args={node.args} />
|
||||
</div>
|
||||
|
||||
<FlowJobResult
|
||||
loading={job['running'] == true}
|
||||
noBorder
|
||||
|
||||
@@ -82,16 +82,10 @@
|
||||
}
|
||||
|
||||
function connectProperty(rawValue: string) {
|
||||
if (isStaticTemplate(inputCat)) {
|
||||
arg.value = `\$\{${rawValue}}`
|
||||
setPropertyType(arg.value)
|
||||
monacoTemplate?.setCode(arg.value)
|
||||
} else {
|
||||
arg.expr = getDefaultExpr(undefined, previousModuleId, rawValue)
|
||||
arg.type = 'javascript'
|
||||
propertyType = 'javascript'
|
||||
monaco?.setCode(arg.expr)
|
||||
}
|
||||
arg.expr = getDefaultExpr(undefined, previousModuleId, rawValue)
|
||||
arg.type = 'javascript'
|
||||
propertyType = 'javascript'
|
||||
monaco?.setCode(arg.expr)
|
||||
}
|
||||
|
||||
function onFocus() {
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
detailed={false}
|
||||
topButton
|
||||
bind:args={stepArgs}
|
||||
isFlow={false}
|
||||
/>
|
||||
{#if testIsLoading}
|
||||
<Button on:click={testJobLoader?.cancelJob} btnClasses="w-full mt-4" color="red" size="sm">
|
||||
|
||||
@@ -277,15 +277,19 @@
|
||||
folderCreated = undefined
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row">
|
||||
<input class="mr-2" placeholder="New folder name" bind:value={newFolderName} />
|
||||
<Button size="md" startIcon={{ icon: faPlus }} disabled={!newFolderName} on:click={addFolder}>
|
||||
New folder
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if folderCreated}
|
||||
<div class="mt-8" />
|
||||
{#if !folderCreated}
|
||||
<div class="flex flex-row">
|
||||
<input class="mr-2" placeholder="New folder name" bind:value={newFolderName} />
|
||||
<Button
|
||||
size="md"
|
||||
startIcon={{ icon: faPlus }}
|
||||
disabled={!newFolderName}
|
||||
on:click={addFolder}
|
||||
>
|
||||
New folder
|
||||
</Button>
|
||||
</div>
|
||||
{:else}
|
||||
<FolderEditor name={folderCreated} />
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
export let loading = false
|
||||
export let noVariablePicker = false
|
||||
export let viewCliRun = false
|
||||
export let isFlow: boolean
|
||||
|
||||
export let args: Record<string, any> = decodeArgs($page.url.searchParams.get('args') ?? undefined)
|
||||
|
||||
@@ -64,9 +65,9 @@
|
||||
let scheduledForStr: string | undefined
|
||||
let invisible_to_owner: false
|
||||
|
||||
$: cliCommand = `wmill ${runnable?.kind} run ${runnable?.path} ${Object.entries(args)
|
||||
.map(([k, v]) => `-i ${k}=${JSON.stringify(v)}`)
|
||||
.join(' ')}`
|
||||
$: cliCommand = `wmill ${isFlow ? 'flow' : 'script'} run ${runnable?.path} -d '${JSON.stringify(
|
||||
args
|
||||
)}'`
|
||||
</script>
|
||||
|
||||
<div class="max-w-6xl">
|
||||
@@ -145,7 +146,7 @@
|
||||
</Button>
|
||||
</div>
|
||||
{#if viewOptions}
|
||||
<div transition:slide class="mt-6">
|
||||
<div transition:slide|local class="mt-6">
|
||||
<div class="border rounded-md p-3 pt-4">
|
||||
<div class="flex flex-row items-end">
|
||||
<div class="w-max md:w-2/3 mt-2 mb-1">
|
||||
@@ -213,14 +214,14 @@
|
||||
<div class="my-10" />
|
||||
<Button
|
||||
color="light"
|
||||
size="sm"
|
||||
size="xs"
|
||||
endIcon={{ icon: viewCliOptions ? faChevronUp : faChevronDown }}
|
||||
on:click={() => (viewCliOptions = !viewCliOptions)}
|
||||
>
|
||||
Run it from the CLI
|
||||
</Button>
|
||||
{#if viewCliOptions}
|
||||
<div transition:slide class="mt-2 px-4 pt-2">
|
||||
<div transition:slide|local class="mt-2 px-4 pt-2">
|
||||
<InlineCodeCopy content={cliCommand} />
|
||||
<CliHelpBox />
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { allTrue } from '$lib/utils'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { slide } from 'svelte/transition'
|
||||
import ArgInput from './ArgInput.svelte'
|
||||
import { Button } from './common'
|
||||
import InputTransformForm from './InputTransformForm.svelte'
|
||||
@@ -69,8 +68,8 @@
|
||||
<div class="w-full {clazz} {flexWrap ? 'flex flex-row flex-wrap gap-x-6 gap-y-2' : ''}">
|
||||
{#if keys.length > 0}
|
||||
{#each keys as argName, i (argName)}
|
||||
{#if !filter || filter.includes(argName)}
|
||||
<div transition:slide|local>
|
||||
{#if (!filter || filter.includes(argName)) && Object.keys(schema.properties ?? {}).includes(argName)}
|
||||
<div>
|
||||
{#if inputTransform}
|
||||
<InputTransformForm
|
||||
{previousModuleId}
|
||||
@@ -84,7 +83,7 @@
|
||||
bind:pickForField
|
||||
{noDynamicToggle}
|
||||
/>
|
||||
{:else if typeof args == 'object'}
|
||||
{:else if typeof args == 'object' && schema?.properties[argName]}
|
||||
<ArgInput
|
||||
autofocus={i == 0 && autofocus}
|
||||
label={argName}
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
kind: script.kind
|
||||
}
|
||||
})
|
||||
sendUserToast(`New script created at hash ${newHash}`)
|
||||
history.replaceState(history.state, '', `/scripts/edit/${newHash}?step=2`)
|
||||
goto(`/scripts/get/${newHash}?workspace_id=${$workspaceStore}`)
|
||||
} catch (error) {
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
export function closeDrawer() {
|
||||
drawer?.closeDrawer()
|
||||
const index = $page.url.href.lastIndexOf('#')
|
||||
if (index === -1) return
|
||||
const hashRemoved = $page.url.href.slice(0, index)
|
||||
goto(hashRemoved)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import { loadIcon } from '../icon'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { goto } from '$app/navigation'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
@@ -32,7 +33,8 @@
|
||||
let runnableComponent: RunnableComponent
|
||||
let disabled: boolean | undefined = undefined
|
||||
let fillContainer: boolean | undefined = undefined
|
||||
let goto: string | undefined = undefined
|
||||
let gotoUrl: string | undefined = undefined
|
||||
let gotoNewTab: boolean | undefined = undefined
|
||||
|
||||
let isLoading: boolean = false
|
||||
let ownClick: boolean = false
|
||||
@@ -87,7 +89,7 @@
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.label} bind:value={labelValue} />
|
||||
<InputValue {id} input={configuration.goto} bind:value={goto} />
|
||||
<InputValue {id} input={configuration.goto} bind:value={gotoUrl} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue {id} input={configuration.beforeIcon} bind:value={beforeIcon} />
|
||||
@@ -101,15 +103,17 @@
|
||||
bind:error={errors.disabled}
|
||||
/>
|
||||
<InputValue {id} input={configuration.fillContainer} bind:value={fillContainer} />
|
||||
<InputValue {id} input={configuration.gotoNewTab} bind:value={gotoNewTab} />
|
||||
|
||||
<RunnableWrapper
|
||||
flexWrap
|
||||
bind:runnableComponent
|
||||
bind:componentInput
|
||||
{componentInput}
|
||||
{id}
|
||||
{extraQueryParams}
|
||||
autoRefresh={false}
|
||||
{goto}
|
||||
goto={gotoUrl}
|
||||
{gotoNewTab}
|
||||
>
|
||||
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
|
||||
{#if errorsMessage}
|
||||
@@ -134,7 +138,18 @@
|
||||
e?.stopPropagation()
|
||||
e?.preventDefault()
|
||||
ownClick = true
|
||||
await runnableComponent?.runComponent()
|
||||
|
||||
if (!runnableComponent) {
|
||||
if (gotoUrl) {
|
||||
if (gotoNewTab) {
|
||||
window.open(gotoUrl, '_blank')
|
||||
} else {
|
||||
goto(gotoUrl)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await runnableComponent?.runComponent()
|
||||
}
|
||||
|
||||
if (recomputeIds) {
|
||||
recomputeIds.forEach((id) => {
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { app, runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, runnableComponents, worldStore, stateId } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let labelValue: string = 'Default label'
|
||||
let color: ButtonType.Color
|
||||
@@ -33,7 +34,8 @@
|
||||
let isLoading: boolean = false
|
||||
|
||||
$: noInputs =
|
||||
componentInput?.type != 'runnable' || Object.keys(componentInput?.fields ?? {}).length == 0
|
||||
$stateId != undefined &&
|
||||
(componentInput?.type != 'runnable' || Object.keys(componentInput?.fields ?? {}).length == 0)
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<Array<any>>
|
||||
@@ -59,9 +61,8 @@
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
|
||||
<RunnableWrapper
|
||||
defaultUserInput
|
||||
bind:runnableComponent
|
||||
bind:componentInput
|
||||
{componentInput}
|
||||
{id}
|
||||
{goto}
|
||||
{extraQueryParams}
|
||||
|
||||
@@ -93,9 +93,8 @@
|
||||
}}
|
||||
>
|
||||
<RunnableWrapper
|
||||
defaultUserInput
|
||||
bind:runnableComponent
|
||||
bind:componentInput
|
||||
{componentInput}
|
||||
{id}
|
||||
{extraQueryParams}
|
||||
autoRefresh={false}
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<InputValue {id} input={configuration.theme} bind:value={theme} />
|
||||
<InputValue {id} input={configuration.line} bind:value={lineChart} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
{#if lineChart}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import { IS_APP_PUBLIC_CONTEXT_KEY, type AppEditorContext, type ComponentCustomCSS } from '../../types'
|
||||
import {
|
||||
IS_APP_PUBLIC_CONTEXT_KEY,
|
||||
type AppEditorContext,
|
||||
type ComponentCustomCSS
|
||||
} from '../../types'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
@@ -18,19 +22,23 @@
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
</script>
|
||||
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<div class={twMerge(
|
||||
'w-full border-b px-2 text-xs p-1 font-semibold bg-gray-500 text-white rounded-t-sm',
|
||||
$app.css?.['displaycomponent']?.['header']?.class,
|
||||
customCss?.header?.class
|
||||
)}>
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<div
|
||||
class={twMerge(
|
||||
'w-full border-b px-2 text-xs p-1 font-semibold bg-gray-500 text-white rounded-t-sm',
|
||||
$app.css?.['displaycomponent']?.['header']?.class,
|
||||
customCss?.header?.class
|
||||
)}
|
||||
>
|
||||
Results
|
||||
</div>
|
||||
<div class={twMerge(
|
||||
'p-2',
|
||||
$app.css?.['displaycomponent']?.['container']?.class,
|
||||
customCss?.container?.class
|
||||
)}>
|
||||
<div
|
||||
class={twMerge(
|
||||
'p-2',
|
||||
$app.css?.['displaycomponent']?.['container']?.class,
|
||||
customCss?.container?.class
|
||||
)}
|
||||
>
|
||||
<DisplayResult {result} {requireHtmlApproval} />
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
bind:clientHeight={h}
|
||||
bind:clientWidth={w}
|
||||
>
|
||||
<RunnableWrapper autoRefresh flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper autoRefresh flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
{#key result}
|
||||
<iframe
|
||||
frameborder="0"
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import { AlignWrapper, InputValue, RunnableWrapper } from '../helpers'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { AlignWrapper, InputValue } from '../helpers'
|
||||
import { loadIcon } from '../icon'
|
||||
|
||||
export let id: string
|
||||
@@ -8,6 +11,9 @@
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = []
|
||||
export let customCss: ComponentCustomCSS<'container' | 'icon'> | undefined = undefined
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let icon: string | undefined = undefined
|
||||
let size: number
|
||||
@@ -22,6 +28,8 @@
|
||||
iconComponent = await loadIcon(icon)
|
||||
}
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.iconcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.icon} bind:value={icon} />
|
||||
@@ -29,13 +37,20 @@
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.strokeWidth} bind:value={strokeWidth} />
|
||||
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
<AlignWrapper
|
||||
{horizontalAlignment}
|
||||
{verticalAlignment}
|
||||
class={css?.container?.class ?? ''}
|
||||
style={css?.container?.style ?? ''}
|
||||
>
|
||||
{#if iconComponent}
|
||||
<svelte:component
|
||||
this={iconComponent}
|
||||
size={size || 24}
|
||||
color={color || 'currentColor'}
|
||||
strokeWidth={strokeWidth || 2}
|
||||
class={css?.icon?.class ?? ''}
|
||||
style={css?.icon?.style ?? ''}
|
||||
/>
|
||||
{/if}
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { staticValues } from '../../editor/componentsPanel/componentStaticValues'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
type FitOption = (typeof staticValues)['objectFitOptions'][number]
|
||||
@@ -8,7 +12,9 @@
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = ['loading']
|
||||
export let customCss: ComponentCustomCSS<'image'> | undefined = undefined
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const fit: Record<FitOption, string> = {
|
||||
cover: 'object-cover',
|
||||
contain: 'object-contain',
|
||||
@@ -18,18 +24,18 @@
|
||||
let source: string | undefined = undefined
|
||||
let imageFit: FitOption | undefined = undefined
|
||||
let altText: string | undefined = undefined
|
||||
let customStyles: string | undefined = undefined
|
||||
|
||||
$: css = concatCustomCss($app.css?.imagecomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.source} bind:value={source} />
|
||||
<InputValue {id} input={configuration.imageFit} bind:value={imageFit} />
|
||||
<InputValue {id} input={configuration.altText} bind:value={altText} />
|
||||
<InputValue {id} input={configuration.customStyles} bind:value={customStyles} />
|
||||
|
||||
<img
|
||||
on:pointerdown|preventDefault
|
||||
src={source}
|
||||
alt={altText}
|
||||
style={customStyles}
|
||||
class="w-full h-full {fit[imageFit || 'cover']}"
|
||||
style={css?.image?.style ?? ''}
|
||||
class={twMerge(`w-full h-full ${fit[imageFit || 'cover']}`, css?.image?.class ?? '')}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { InputValue } from '../helpers'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Map, View, Feature } from 'ol'
|
||||
import { useGeographic } from 'ol/proj'
|
||||
import { OSM, Vector as VectorSource } from 'ol/source'
|
||||
import { Vector as VectorLayer, Tile as TileLayer } from 'ol/layer'
|
||||
import { Point } from 'ol/geom'
|
||||
import { defaults as defaultControls } from 'ol/control'
|
||||
import { findGridItem } from '../../editor/appUtils'
|
||||
import type { Output } from '../../rx'
|
||||
|
||||
interface Marker {
|
||||
lon: number
|
||||
lat: number
|
||||
title?: string
|
||||
radius?: number
|
||||
color?: string
|
||||
strokeWidth?: number
|
||||
strokeColor?: string
|
||||
}
|
||||
|
||||
const LAYER_NAME = {
|
||||
MARKER: 'Marker'
|
||||
} as const
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = ['mapRegion']
|
||||
export let customCss: ComponentCustomCSS<'map'> | undefined = undefined
|
||||
|
||||
const { app, worldStore, selectedComponent, connectingInput, focusedGrid, mode } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
mapRegion: Output<{
|
||||
topLeft: { lat: number; lon: number }
|
||||
bottomRight: { lat: number; lon: number }
|
||||
}>
|
||||
}
|
||||
|
||||
let map: Map
|
||||
let mapElement: HTMLDivElement
|
||||
|
||||
let longitude: number | undefined = undefined
|
||||
let latitude: number | undefined = undefined
|
||||
let zoom: number | undefined = undefined
|
||||
// If string, it's a JSON file read as text
|
||||
let markers: Marker[] | string | undefined = undefined
|
||||
|
||||
$: if (map && longitude && latitude) {
|
||||
map.getView().setCenter([longitude, latitude])
|
||||
}
|
||||
|
||||
$: if (map && zoom) {
|
||||
map.getView().setZoom(zoom)
|
||||
}
|
||||
|
||||
$: if (map && markers) {
|
||||
updateMarkers()
|
||||
}
|
||||
|
||||
function selectComponent() {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
$focusedGrid = undefined
|
||||
}
|
||||
}
|
||||
|
||||
function getLayersByName(name: keyof typeof LAYER_NAME) {
|
||||
return map
|
||||
.getLayers()
|
||||
.getArray()
|
||||
.filter((l) => l.getProperties().name === LAYER_NAME[name])
|
||||
}
|
||||
|
||||
function getMarkerArray(): Marker[] | undefined {
|
||||
let array: Marker[] | undefined = undefined
|
||||
if (typeof markers === 'string') {
|
||||
try {
|
||||
array = JSON.parse(markers)
|
||||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
} else {
|
||||
array = markers
|
||||
}
|
||||
return array?.filter((m) => !isNaN(+m.lat) && !isNaN(+m.lon))
|
||||
}
|
||||
|
||||
function createMarkerLayers() {
|
||||
const markerArray = getMarkerArray()
|
||||
return markerArray?.map((m) => {
|
||||
return new VectorLayer({
|
||||
properties: {
|
||||
name: LAYER_NAME.MARKER
|
||||
},
|
||||
source: new VectorSource({
|
||||
features: [
|
||||
new Feature({
|
||||
geometry: new Point([+m.lon, +m.lat]),
|
||||
name: m.title
|
||||
})
|
||||
]
|
||||
}),
|
||||
style: {
|
||||
'circle-radius': m.radius ?? 7,
|
||||
'circle-fill-color': m.color ?? '#dc2626',
|
||||
'circle-stroke-width': m.strokeWidth ?? 3,
|
||||
'circle-stroke-color': m.strokeColor ?? '#fca5a5'
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function updateMarkers() {
|
||||
const layers = getLayersByName('MARKER')
|
||||
if (layers?.length) {
|
||||
layers.forEach((l) => map.removeLayer(l))
|
||||
}
|
||||
createMarkerLayers()?.forEach((l) => map.addLayer(l))
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
useGeographic()
|
||||
map = new Map({
|
||||
target: mapElement,
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new OSM()
|
||||
}),
|
||||
...(createMarkerLayers() || [])
|
||||
],
|
||||
view: new View({
|
||||
center: [longitude ?? 0, latitude ?? 0],
|
||||
zoom: zoom ?? 2
|
||||
}),
|
||||
controls: defaultControls({
|
||||
attribution: false
|
||||
})
|
||||
})
|
||||
updateRegionOutput()
|
||||
})
|
||||
|
||||
$: css = concatCustomCss($app.css?.mapcomponent, customCss)
|
||||
$: gridItem = findGridItem($app, id)
|
||||
|
||||
function updateRegionOutput() {
|
||||
if (map) {
|
||||
let extent = map.getView().calculateExtent(map.getSize())
|
||||
const [left, bottom, right, top] = extent
|
||||
|
||||
if (outputs?.mapRegion) {
|
||||
outputs.mapRegion.set({
|
||||
topLeft: { lat: top, lon: left },
|
||||
bottomRight: { lat: bottom, lon: right }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleSyncRegion() {
|
||||
if (!map || !gridItem) {
|
||||
return
|
||||
}
|
||||
|
||||
const z = map.getView().getZoom()
|
||||
updateRegionOutput()
|
||||
|
||||
if (z) {
|
||||
gridItem.data.configuration.zoom.value = z
|
||||
}
|
||||
|
||||
const center = map.getView().getCenter()
|
||||
if (!center) {
|
||||
return
|
||||
}
|
||||
|
||||
if (gridItem) {
|
||||
gridItem.data.configuration.longitude.value = center[0]
|
||||
gridItem.data.configuration.latitude.value = center[1]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.longitude} bind:value={longitude} />
|
||||
<InputValue {id} input={configuration.latitude} bind:value={latitude} />
|
||||
<InputValue {id} input={configuration.zoom} bind:value={zoom} />
|
||||
<InputValue {id} input={configuration.markers} bind:value={markers} />
|
||||
|
||||
<div class="relative h-full w-full">
|
||||
<div
|
||||
on:pointerdown|stopPropagation={selectComponent}
|
||||
bind:this={mapElement}
|
||||
class={twMerge(`w-full h-full`, css?.map?.class ?? '')}
|
||||
style={css?.map?.style ?? ''}
|
||||
/>
|
||||
|
||||
{#if $mode !== 'preview'}
|
||||
<div
|
||||
class="absolute bottom-0 left-0 px-1 py-0.5 bg-indigo-500 text-white text-2xs"
|
||||
on:pointerdown={handleSyncRegion}
|
||||
>
|
||||
Set region
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style global lang="postcss">
|
||||
.ol-overlaycontainer-stopevent {
|
||||
@apply flex flex-col justify-start items-end;
|
||||
}
|
||||
.ol-control button {
|
||||
@apply w-7 h-7 center-center bg-white border border-gray-300 text-gray-700
|
||||
rounded mt-1 mr-1 shadow duration-200 hover:bg-gray-100 focus:bg-gray-100
|
||||
hover:border-gray-500 focus:border-gray-500;
|
||||
}
|
||||
</style>
|
||||
@@ -72,7 +72,7 @@
|
||||
<InputValue {id} input={configuration.theme} bind:value={theme} />
|
||||
<InputValue {id} input={configuration.doughnutStyle} bind:value={doughnut} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
{#if doughnut}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<InputValue {id} input={configuration.zoomable} bind:value={zoomable} />
|
||||
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
<Scatter {data} {options} />
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<InputValue {id} input={configuration.style} bind:value={style} />
|
||||
<InputValue {id} input={configuration.copyButton} bind:value={copyButton} />
|
||||
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
{#if !result || result === ''}
|
||||
<div class="text-gray-400 bg-gray-100 flex justify-center items-center h-full w-full">
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<InputValue {id} input={configuration.zoomable} bind:value={zoomable} />
|
||||
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
<Scatter {data} {options} />
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<InputValue {id} input={configuration.canvas} bind:value={canvas} />
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
export { default as AppTable } from './table/AppTable.svelte'
|
||||
export { default as AppAggridTable } from './table/AppAggridTable.svelte'
|
||||
export { default as AppBarChart } from './AppBarChart.svelte'
|
||||
export { default as AppDisplayComponent } from './AppDisplayComponent.svelte'
|
||||
export { default as AppHtml } from './AppHtml.svelte'
|
||||
export { default as AppIcon } from './AppIcon.svelte'
|
||||
export { default as AppImage } from './AppImage.svelte'
|
||||
export { default as AppMap } from './AppMap.svelte'
|
||||
export { default as AppPieChart } from './AppPieChart.svelte'
|
||||
export { default as AppScatterChart } from './AppScatterChart.svelte'
|
||||
export { default as AppText } from './AppText.svelte'
|
||||
export { default as AppTimeseries } from './AppTimeseries.svelte'
|
||||
export { default as PlotlyHtml } from './PlotlyHtml.svelte'
|
||||
export { default as VegaLiteHtml } from './VegaLiteHtml.svelte'
|
||||
export { default as VegaLiteHtml } from './VegaLiteHtml.svelte'
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<InputValue {id} input={configuration.pagination} bind:value={pagination} />
|
||||
<InputValue {id} input={configuration.pageSize} bind:value={pageSize} />
|
||||
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
{#if Array.isArray(result) && result.every(isObject)}
|
||||
<div
|
||||
class="border border-gray-300 shadow-sm divide-y divide-gray-300 flex flex-col h-full"
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
<InputValue {id} input={configuration.search} bind:value={search} />
|
||||
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
{#if Array.isArray(result) && result.every(isObject)}
|
||||
<div
|
||||
class={twMerge(
|
||||
|
||||
@@ -1,41 +1,44 @@
|
||||
<script lang="ts">
|
||||
import { classNames } from '$lib/utils'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { HorizontalAlignment, VerticalAlignment } from '../../types'
|
||||
|
||||
export let horizontalAlignment: HorizontalAlignment | undefined = undefined
|
||||
export let verticalAlignment: VerticalAlignment | undefined = undefined
|
||||
export let noWFull = false
|
||||
let c = ''
|
||||
export { c as class }
|
||||
export let style = ''
|
||||
|
||||
function tailwindHorizontalAlignment(alignment?: HorizontalAlignment) {
|
||||
if(!alignment) return '';
|
||||
if (!alignment) return ''
|
||||
const classes: Record<HorizontalAlignment, string> = {
|
||||
left: 'justify-start',
|
||||
center: 'justify-center',
|
||||
right: 'justify-end',
|
||||
right: 'justify-end'
|
||||
}
|
||||
return classes[alignment]
|
||||
}
|
||||
|
||||
function tailwindVerticalAlignment(alignment?: VerticalAlignment) {
|
||||
if(!alignment) return '';
|
||||
if (!alignment) return ''
|
||||
const classes: Record<VerticalAlignment, string> = {
|
||||
top: 'items-start',
|
||||
center: 'items-center',
|
||||
bottom: 'items-end',
|
||||
bottom: 'items-end'
|
||||
}
|
||||
return classes[alignment]
|
||||
}
|
||||
|
||||
$: classes = classNames(
|
||||
$: classes = twMerge(
|
||||
'flex z-auto',
|
||||
noWFull ? '' : 'w-full',
|
||||
tailwindHorizontalAlignment(horizontalAlignment),
|
||||
tailwindVerticalAlignment(verticalAlignment),
|
||||
verticalAlignment ? 'h-full' : '',
|
||||
$$props.class || ''
|
||||
c
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class={classes}>
|
||||
<div class={classes} {style}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { ConnectedAppInput, RowAppInput, StaticAppInput, UserAppInput } from '../../inputType'
|
||||
import type {
|
||||
ConnectedAppInput,
|
||||
RowAppInput,
|
||||
StaticAppInput,
|
||||
UserAppInput
|
||||
} from '../../inputType'
|
||||
import type { InlineScript } from '../../types'
|
||||
import RunnableComponent from './RunnableComponent.svelte'
|
||||
|
||||
@@ -15,7 +20,7 @@
|
||||
|
||||
<RunnableComponent
|
||||
{id}
|
||||
bind:fields
|
||||
{fields}
|
||||
bind:result
|
||||
runnable={{
|
||||
name,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { isCodeInjection } from '$lib/components/flows/utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput, EvalAppInput, UploadAppInput } from '../../inputType'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
@@ -11,24 +12,33 @@
|
||||
export let value: T
|
||||
export let id: string | undefined = undefined
|
||||
export let error: string = ''
|
||||
|
||||
let lastInput = input ? JSON.parse(JSON.stringify(input)) : undefined
|
||||
|
||||
$: if (input && !deepEqual(input, lastInput)) {
|
||||
lastInput = JSON.parse(JSON.stringify(input))
|
||||
}
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
$: state = $worldStore?.state
|
||||
$: input && $worldStore && handleConnection()
|
||||
$: input && input.type == 'template' && $state && (value = getValue(input))
|
||||
$: input && input.type == 'eval' && $state && (value = evalExpr(input))
|
||||
|
||||
$: lastInput && $worldStore && handleConnection()
|
||||
$: lastInput && lastInput.type == 'template' && $state && (value = getValue(lastInput))
|
||||
$: lastInput && lastInput.type == 'eval' && $state && (value = evalExpr(lastInput))
|
||||
|
||||
function handleConnection() {
|
||||
if (input.type === 'connected') {
|
||||
$worldStore?.connect<any>(input, onValueChange)
|
||||
} else if (input.type === 'static' || input.type == 'template') {
|
||||
setTimeout(() => (value = getValue(input)), 0)
|
||||
} else if (input.type == 'eval') {
|
||||
setTimeout(() => ((value = evalExpr(input as EvalAppInput)), 0))
|
||||
} else if (input.type == 'upload') {
|
||||
setTimeout(() => ((value = (input as UploadAppInput).value), 0))
|
||||
// console.log('handle connection', id, lastInput)
|
||||
if (lastInput.type === 'connected') {
|
||||
$worldStore?.connect<any>(lastInput, onValueChange)
|
||||
} else if (lastInput.type === 'static' || lastInput.type == 'template') {
|
||||
value = getValue(lastInput)
|
||||
} else if (lastInput.type == 'eval') {
|
||||
value = evalExpr(lastInput as EvalAppInput)
|
||||
} else if (lastInput.type == 'upload') {
|
||||
value = (lastInput as UploadAppInput).value
|
||||
} else {
|
||||
setTimeout(() => (value = undefined), 0)
|
||||
value = undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +108,8 @@
|
||||
}
|
||||
|
||||
function onValueChange(newValue: any): void {
|
||||
if (input.type === 'connected' && newValue !== undefined && newValue !== null) {
|
||||
const { connection } = input
|
||||
if (lastInput.type === 'connected' && newValue !== undefined && newValue !== null) {
|
||||
const { connection } = lastInput
|
||||
|
||||
if (!connection) {
|
||||
// No connection
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { AppService, type CompletedJob } from '$lib/gen'
|
||||
import { defaultIfEmptyString, emptySchema, sendUserToast } from '$lib/utils'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { computeFields } from '../../editor/inlineScriptsPanel/utils'
|
||||
import type { AppInputs, Runnable } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
@@ -22,12 +21,12 @@
|
||||
export let autoRefresh: boolean = true
|
||||
export let result: any = undefined
|
||||
export let forceSchemaDisplay: boolean = false
|
||||
export let defaultUserInput = false
|
||||
export let flexWrap = false
|
||||
export let wrapperClass = ''
|
||||
export let wrapperStyle = ''
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let gotoUrl: string | undefined = undefined
|
||||
export let gotoNewTab: boolean | undefined = undefined
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
@@ -38,7 +37,8 @@
|
||||
jobs,
|
||||
noBackend,
|
||||
errorByComponent,
|
||||
mode
|
||||
mode,
|
||||
stateId
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
onMount(() => {
|
||||
@@ -105,29 +105,9 @@
|
||||
|
||||
$: outputs?.loading?.set(testIsLoading)
|
||||
|
||||
$: runnable?.type === 'runnableByName' && loadSchemaAndInputsByName()
|
||||
$: schemaStripped = stripSchema(fields, $stateId)
|
||||
|
||||
async function loadSchemaAndInputsByName() {
|
||||
if (runnable?.type === 'runnableByName') {
|
||||
const { inlineScript } = runnable
|
||||
// Inline scripts directly provide the schema
|
||||
if (inlineScript) {
|
||||
const newSchema = inlineScript.schema
|
||||
|
||||
const newFields = computeFields(newSchema, defaultUserInput, fields)
|
||||
|
||||
if (JSON.stringify(newFields) !== JSON.stringify(fields)) {
|
||||
setTimeout(() => {
|
||||
fields = newFields
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: schemaStripped = runnable && stripSchema(fields)
|
||||
|
||||
function stripSchema(inputs: AppInputs): Schema {
|
||||
function stripSchema(inputs: AppInputs, s: any): Schema {
|
||||
let schema =
|
||||
runnable?.type == 'runnableByName' ? runnable.inlineScript?.schema : runnable?.schema
|
||||
try {
|
||||
@@ -262,7 +242,13 @@
|
||||
delete $errorByComponent[previousJobId]
|
||||
$errorByComponent = $errorByComponent
|
||||
}
|
||||
gotoUrl && gotoUrl != '' && result?.error == undefined && goto(gotoUrl)
|
||||
if (gotoUrl && gotoUrl != '' && result?.error == undefined) {
|
||||
if (gotoNewTab) {
|
||||
window.open(gotoUrl, '_blank')
|
||||
} else {
|
||||
goto(gotoUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -272,11 +258,6 @@
|
||||
/>
|
||||
|
||||
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}" style={wrapperStyle}>
|
||||
{#if !initializing && autoRefresh === true}
|
||||
<div class="flex absolute top-1 right-1">
|
||||
<RefreshButton componentId={id} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if schemaStripped && Object.keys(schemaStripped?.properties ?? {}).length > 0 && (autoRefresh || forceSchemaDisplay)}
|
||||
<div class="px-2 h-fit min-h-0">
|
||||
<SchemaForm
|
||||
@@ -312,4 +293,9 @@
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
{#if !initializing && autoRefresh === true}
|
||||
<div class="flex absolute top-1 right-1 z-50">
|
||||
<RefreshButton componentId={id} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
export let autoRefresh: boolean = true
|
||||
export let runnableComponent: RunnableComponent | undefined = undefined
|
||||
export let forceSchemaDisplay: boolean = false
|
||||
export let defaultUserInput = false
|
||||
export let flexWrap = false
|
||||
export let runnableClass = ''
|
||||
export let runnableStyle = ''
|
||||
export let goto: string | undefined = undefined
|
||||
export let gotoNewTab: boolean | undefined = undefined
|
||||
|
||||
const { staticExporter, noBackend } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
@@ -44,10 +44,10 @@
|
||||
{:else if componentInput.type === 'runnable' && isRunnableDefined()}
|
||||
<RunnableComponent
|
||||
gotoUrl={goto}
|
||||
{gotoNewTab}
|
||||
{flexWrap}
|
||||
{defaultUserInput}
|
||||
bind:this={runnableComponent}
|
||||
bind:fields={componentInput.fields}
|
||||
fields={componentInput.fields}
|
||||
bind:result
|
||||
runnable={componentInput.runnable}
|
||||
{autoRefresh}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
@@ -12,8 +14,9 @@
|
||||
export let inputType: 'date' | 'time' | 'datetime-local'
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let input: HTMLInputElement
|
||||
let labelValue: string = 'Title'
|
||||
let minValue: string = ''
|
||||
@@ -29,6 +32,8 @@
|
||||
}
|
||||
|
||||
$: input && handleInput()
|
||||
|
||||
$: css = concatCustomCss($app.css?.dateinputcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.label} bind:value={labelValue} />
|
||||
@@ -46,6 +51,7 @@
|
||||
min={minValue}
|
||||
max={maxValue}
|
||||
placeholder="Type..."
|
||||
class="h-full"
|
||||
class={twMerge('mx-0.5', css?.input?.class ?? '')}
|
||||
style={css?.input?.style ?? ''}
|
||||
/>
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { FileInput } from '../../../common'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let acceptedFileTypes: string[] | undefined = undefined
|
||||
let allowMultiple: boolean | undefined = undefined
|
||||
@@ -24,6 +27,8 @@
|
||||
async function handleChange(files: string[] | undefined) {
|
||||
outputs?.result.set(files)
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.fileinputcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.acceptedFileTypes} bind:value={acceptedFileTypes} />
|
||||
@@ -34,11 +39,12 @@
|
||||
<FileInput
|
||||
accept={acceptedFileTypes?.length ? acceptedFileTypes?.join(', ') : undefined}
|
||||
multiple={allowMultiple}
|
||||
convertToBase64
|
||||
convertTo="base64"
|
||||
on:change={({ detail }) => {
|
||||
handleChange(detail)
|
||||
}}
|
||||
class="w-full h-full"
|
||||
class={twMerge('w-full h-full', css?.container?.class)}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
{text}
|
||||
</FileInput>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import Select from 'svelte-select'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '../../../../defaults'
|
||||
import { stopPropagation } from 'ol/events/Event'
|
||||
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
|
||||
const { app, worldStore, connectingInput, selectedComponent } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
let items: { label: string; value: string }[]
|
||||
let placeholder: string = 'Select an item'
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<string[] | undefined>
|
||||
}
|
||||
|
||||
// $: outputs && handleOutputs()
|
||||
|
||||
// function handleOutputs() {
|
||||
// value = outputs.result.peak()
|
||||
// }
|
||||
|
||||
let value: { value: string }[] | undefined = outputs?.result.peak()
|
||||
|
||||
$: labels && handleItems()
|
||||
|
||||
let labels: string[] | undefined = []
|
||||
|
||||
function handleItems() {
|
||||
if (labels) {
|
||||
items = labels?.map((label) => {
|
||||
return {
|
||||
label,
|
||||
value: label
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$: value && outputs?.result.set(value.map((v) => v.value))
|
||||
|
||||
$: css = concatCustomCss($app.css?.selectcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.items} bind:value={labels} />
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
<div class="app-select w-full mx-0.5" style="height: 34px" on:pointerdown|stopPropagation>
|
||||
{#if !value || Array.isArray(value)}
|
||||
<Select
|
||||
multiple
|
||||
on:change={(e) => e.stopPropagation()}
|
||||
{items}
|
||||
class={css?.input?.class ?? ''}
|
||||
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
||||
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles + css?.input?.style}
|
||||
bind:value
|
||||
{placeholder}
|
||||
on:click={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
}
|
||||
}}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
floatingConfig={{
|
||||
strategy: 'fixed'
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
Value {value} is not an array
|
||||
{/if}
|
||||
</div>
|
||||
</AlignWrapper>
|
||||
|
||||
<style global>
|
||||
.app-select .value-container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.svelte-select-list {
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -2,7 +2,8 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
@@ -11,8 +12,9 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let input: HTMLInputElement
|
||||
|
||||
let defaultValue: number | undefined = undefined
|
||||
@@ -33,6 +35,8 @@
|
||||
}
|
||||
|
||||
$: input && handleInput()
|
||||
|
||||
$: css = concatCustomCss($app.css?.numberinputcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.step} bind:value={step} />
|
||||
@@ -44,6 +48,8 @@
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<input
|
||||
class="mx-0.5 {css?.input?.class ?? ''}"
|
||||
style={css?.input?.style ?? ''}
|
||||
bind:this={input}
|
||||
on:input={handleInput}
|
||||
on:focus={(e) => {
|
||||
|
||||
@@ -3,20 +3,24 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { Badge } from '$lib/components/common'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'handles' | 'bar' | 'limits' | 'values'> | undefined =
|
||||
undefined
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let min = 0
|
||||
let max = 42
|
||||
let step = 1
|
||||
let slider: HTMLElement
|
||||
|
||||
let values: [number, number] = [0, 0]
|
||||
|
||||
@@ -25,6 +29,17 @@
|
||||
}
|
||||
|
||||
$: outputs?.result.set(values)
|
||||
|
||||
$: css = concatCustomCss($app.css?.rangecomponent, customCss)
|
||||
|
||||
let lastStyle: string | undefined = undefined
|
||||
$: if (css && slider && lastStyle !== css?.handles?.style) {
|
||||
const handles = slider.querySelectorAll<HTMLSpanElement>('.rangeNub')
|
||||
if (handles) {
|
||||
lastStyle = css?.handles?.style
|
||||
handles.forEach((handle) => (handle.style.cssText = css?.handles?.style ?? ''))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.step} bind:value={step} />
|
||||
@@ -36,20 +51,43 @@
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex items-center w-full gap-1 px-1">
|
||||
<span>{min}</span>
|
||||
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
|
||||
{+min}
|
||||
</span>
|
||||
<div class="grow" on:pointerdown|stopPropagation>
|
||||
<RangeSlider {step} range min={min ?? 0} max={max ?? 1} bind:values />
|
||||
<!-- <DoubleRange bind:start bind:end /> -->
|
||||
<RangeSlider
|
||||
bind:slider
|
||||
bind:values
|
||||
{step}
|
||||
min={!min ? 0 : +min}
|
||||
max={!max ? 1 : +max}
|
||||
range
|
||||
/>
|
||||
<!-- <RangeSlider {step} range min={min ?? 0} max={max ?? 1} bind:values /> -->
|
||||
</div>
|
||||
<span>{max}</span>
|
||||
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
|
||||
{+max}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between px-1">
|
||||
<div>
|
||||
<Badge color="blue">{values[0]}</Badge>
|
||||
</div>
|
||||
<div>
|
||||
<Badge color="blue">{values[1]}</Badge>
|
||||
</div>
|
||||
<span
|
||||
class={twMerge(
|
||||
'text-center text-sm font-medium bg-blue-100 text-blue-800 rounded px-2.5 py-0.5',
|
||||
css?.values?.class ?? ''
|
||||
)}
|
||||
style={css?.values?.style ?? ''}
|
||||
>
|
||||
{values[0]}
|
||||
</span>
|
||||
<span
|
||||
class={twMerge(
|
||||
'text-center text-sm font-medium bg-blue-100 text-blue-800 rounded px-2.5 py-0.5',
|
||||
css?.values?.class ?? ''
|
||||
)}
|
||||
style={css?.values?.style ?? ''}
|
||||
>
|
||||
{values[1]}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -18,25 +18,18 @@
|
||||
|
||||
const { app, worldStore, connectingInput, selectedComponent } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
let items: { label: string; value: any }[]
|
||||
let multiple: boolean = false
|
||||
let items: { label: string; value: any; created?: boolean }[]
|
||||
let placeholder: string = 'Select an item'
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<string | undefined>
|
||||
result: Output<any | undefined>
|
||||
}
|
||||
|
||||
// $: outputs && handleOutputs()
|
||||
|
||||
// function handleOutputs() {
|
||||
// value = outputs.result.peak()
|
||||
// }
|
||||
|
||||
let value: string | undefined = outputs?.result.peak()
|
||||
|
||||
$: items && handleItems()
|
||||
|
||||
let listItems: { label: string; value: string }[] = []
|
||||
let listItems: { label: string; value: string; created?: boolean }[] = []
|
||||
|
||||
function handleItems() {
|
||||
listItems = Array.isArray(items)
|
||||
@@ -47,21 +40,22 @@
|
||||
}
|
||||
})
|
||||
: []
|
||||
const valuePeak = outputs?.result.peak()
|
||||
if (!valuePeak) {
|
||||
const value0 = items?.[0]?.['value']
|
||||
if (value0 != value) {
|
||||
value = JSON.stringify(value0)
|
||||
outputs?.result.set(value0)
|
||||
}
|
||||
} else {
|
||||
value = JSON.stringify(valuePeak)
|
||||
if (defaultValue) {
|
||||
value = JSON.stringify(defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
function onChange(e: CustomEvent) {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
|
||||
if (create) {
|
||||
listItems = listItems.map((i) => {
|
||||
delete i.created
|
||||
return i
|
||||
})
|
||||
}
|
||||
|
||||
let result: any = undefined
|
||||
try {
|
||||
result = JSON.parse(e.detail?.['value'])
|
||||
@@ -70,22 +64,49 @@
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.selectcomponent, customCss)
|
||||
|
||||
let defaultValue: any = undefined
|
||||
|
||||
function handleFilter(e) {
|
||||
if (create) {
|
||||
if (e.detail.length === 0 && filterText.length > 0) {
|
||||
const prev = listItems.filter((i) => !i.created)
|
||||
listItems = [
|
||||
...prev,
|
||||
{ value: JSON.stringify(filterText), label: filterText, created: true }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: defaultValue && handleDefault()
|
||||
|
||||
function handleDefault() {
|
||||
if (defaultValue) {
|
||||
value = JSON.stringify(defaultValue)
|
||||
outputs?.result.set(defaultValue)
|
||||
}
|
||||
}
|
||||
let create = false
|
||||
let filterText = ''
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.items} bind:value={items} />
|
||||
<InputValue {id} input={configuration.multiple} bind:value={multiple} />
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
<InputValue {id} input={configuration.create} bind:value={create} />
|
||||
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
<div class="app-select w-full mx-0.5" style="height: 34px" on:pointerdown|stopPropagation>
|
||||
<Select
|
||||
{multiple}
|
||||
bind:filterText
|
||||
on:filter={handleFilter}
|
||||
on:clear={onChange}
|
||||
on:change={onChange}
|
||||
items={listItems}
|
||||
class={css?.input?.class ?? ''}
|
||||
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
||||
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles + css?.input.style}
|
||||
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles + css?.input?.style}
|
||||
{value}
|
||||
{placeholder}
|
||||
on:click={() => {
|
||||
@@ -99,7 +120,15 @@
|
||||
floatingConfig={{
|
||||
strategy: 'fixed'
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<div slot="item" let:item>
|
||||
{#if create}
|
||||
{item.created ? 'Add new: ' : ''}
|
||||
{/if}
|
||||
|
||||
{item.label}
|
||||
</div>
|
||||
</Select>
|
||||
</div>
|
||||
</AlignWrapper>
|
||||
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { Badge } from '$lib/components/common'
|
||||
import RangeSlider from 'svelte-range-slider-pips'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'handle' | 'limits' | 'value'> | undefined = undefined
|
||||
|
||||
const { worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let min = 0
|
||||
let max = 42
|
||||
let step = 1
|
||||
let slider: HTMLElement
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<number | null>
|
||||
@@ -34,6 +37,17 @@
|
||||
const num = isNaN(+values[0]) ? null : +values[0]
|
||||
outputs?.result.set(num)
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.slidercomponent, customCss)
|
||||
|
||||
let lastStyle: string | undefined = undefined
|
||||
$: if (css && slider && lastStyle !== css?.handle?.style) {
|
||||
const handle = slider.querySelector<HTMLSpanElement>('.rangeNub')
|
||||
if (handle) {
|
||||
lastStyle = css?.handle?.style
|
||||
handle.style.cssText = css?.handle?.style ?? ''
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.step} bind:value={step} />
|
||||
@@ -43,11 +57,25 @@
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<div class="flex items-center w-full gap-1 px-1">
|
||||
<span>{min}</span>
|
||||
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
|
||||
{+min}
|
||||
</span>
|
||||
<div class="grow" on:pointerdown|stopPropagation={() => ($selectedComponent = id)}>
|
||||
<RangeSlider {step} min={min ?? 0} max={max ?? 1} bind:values />
|
||||
<RangeSlider bind:slider bind:values {step} min={+min} max={+max} />
|
||||
</div>
|
||||
<span>{max}</span>
|
||||
<span class="mx-2"><Badge large color="blue">{values[0]}</Badge></span>
|
||||
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
|
||||
{+max}
|
||||
</span>
|
||||
<span class="mx-2">
|
||||
<span
|
||||
class={twMerge(
|
||||
'text-center text-sm font-medium bg-blue-100 text-blue-800 rounded px-2.5 py-0.5',
|
||||
css?.value?.class ?? ''
|
||||
)}
|
||||
style={css?.value?.style ?? ''}
|
||||
>
|
||||
{values[0]}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
@@ -14,6 +15,7 @@
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let appCssKey: 'textinputcomponent' | 'passwordinputcomponent' = 'textinputcomponent'
|
||||
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let input: HTMLInputElement
|
||||
@@ -31,7 +33,7 @@
|
||||
|
||||
$: input && handleInput()
|
||||
|
||||
$: css = concatCustomCss($app.css?.textinputcomponent, customCss)
|
||||
$: css = concatCustomCss($app.css?.[appCssKey], customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
@@ -40,7 +42,7 @@
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<input
|
||||
class="mx-0.5 {css?.input?.class ?? ''}"
|
||||
class={twMerge('mx-0.5', css?.input?.class ?? '')}
|
||||
style={css?.input?.style ?? ''}
|
||||
on:focus={(e) => {
|
||||
e?.stopPropagation()
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppEditorContext } from '../../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../../types'
|
||||
import { concatCustomCss } from '../../../utils'
|
||||
import AlignWrapper from '../../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import CurrencyInput from './CurrencyInput.svelte'
|
||||
@@ -11,8 +12,9 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let defaultValue: number | undefined = undefined
|
||||
|
||||
@@ -37,6 +39,8 @@
|
||||
$: value != undefined && handleInput()
|
||||
|
||||
$: defaultValue != undefined && handleDefault()
|
||||
|
||||
$: css = concatCustomCss($app.css?.currencycomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
@@ -49,7 +53,12 @@
|
||||
{#key locale}
|
||||
{#key currency}
|
||||
<CurrencyInput
|
||||
inputClasses={{ formatted: 'p-0', wrapper: 'w-full', formattedZero: 'text-black' }}
|
||||
inputClasses={{
|
||||
formatted: 'p-0 ' + css?.input?.class,
|
||||
wrapper: 'w-full',
|
||||
formattedZero: 'text-black ' + css?.input?.class
|
||||
}}
|
||||
style={css?.input?.style}
|
||||
bind:value
|
||||
{currency}
|
||||
{locale}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
export let isNegativeAllowed: boolean = true
|
||||
export let fractionDigits: number = DEFAULT_FRACTION_DIGITS
|
||||
export let inputClasses: InputClasses | null = null
|
||||
export let style: string = ''
|
||||
|
||||
// Formats value as: e.g. $1,523.00 | -$1,523.00
|
||||
const formatCurrency = (
|
||||
@@ -175,6 +176,7 @@
|
||||
? inputClasses?.formattedNegative ?? DEFAULT_CLASS_FORMATTED_NEGATIVE
|
||||
: ''}
|
||||
"
|
||||
{style}
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
name={`formatted-${name}`}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
export const staticOutputs: string[] = []
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let gridContent: string[] | undefined = undefined
|
||||
|
||||
function onFocus() {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
@@ -31,17 +29,19 @@
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
|
||||
<InputValue {id} input={configuration.gridContent} bind:value={gridContent} />
|
||||
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
{noPadding}
|
||||
class={css?.container.class}
|
||||
style={css?.container.style}
|
||||
bind:subGrid={$app.subgrids[`${id}-0`]}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<div class="border">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
{noPadding}
|
||||
{id}
|
||||
class={css?.container.class}
|
||||
style={css?.container.style}
|
||||
bind:subGrid={$app.subgrids[`${id}-0`]}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { HorizontalAlignment, VerticalAlignment } from '../../types'
|
||||
import type {
|
||||
AppEditorContext,
|
||||
ComponentCustomCSS,
|
||||
HorizontalAlignment,
|
||||
VerticalAlignment
|
||||
} from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
@@ -8,17 +16,34 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let horizontalAlignment: HorizontalAlignment | undefined = undefined
|
||||
export let verticalAlignment: VerticalAlignment | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container' | 'divider'> | undefined = undefined
|
||||
export let position: 'horizontal' | 'vertical'
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let size = 2
|
||||
let color = '#00000060'
|
||||
|
||||
$: css = concatCustomCss($app.css?.[position + 'dividercomponent'], customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment} class="h-full">
|
||||
<AlignWrapper
|
||||
{horizontalAlignment}
|
||||
{verticalAlignment}
|
||||
class={twMerge(css?.container?.class, 'h-full')}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
<div
|
||||
class="rounded-full {position === 'horizontal' ? 'w-full' : 'h-full'}"
|
||||
style="{position === 'horizontal' ? 'height' : 'width'}: {size}px; background-color: {color}"
|
||||
class={twMerge(
|
||||
`rounded-full ${position === 'horizontal' ? 'w-full' : 'h-full'}`,
|
||||
css?.divider?.class ?? ''
|
||||
)}
|
||||
style="
|
||||
{position === 'horizontal' ? 'height' : 'width'}: {size}px;
|
||||
background-color: {color};
|
||||
{css?.divider?.style ?? ''}
|
||||
"
|
||||
/>
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import Portal from 'svelte-portal'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { Button, ButtonType, Drawer, DrawerContent } from '$lib/components/common'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { AlignWrapper } from '../helpers'
|
||||
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let noWFull = false
|
||||
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let gridContent: string[] | undefined = undefined
|
||||
let noPadding: boolean | undefined = undefined
|
||||
let drawerTitle: string | undefined = undefined
|
||||
let appDrawer: Drawer
|
||||
|
||||
let labelValue: string
|
||||
let color: ButtonType.Color
|
||||
let size: ButtonType.Size
|
||||
let disabled: boolean | undefined = undefined
|
||||
let fillContainer: boolean | undefined = undefined
|
||||
|
||||
$: css = concatCustomCss($app.css?.containercomponent, customCss)
|
||||
</script>
|
||||
|
||||
<div class="h-full w-full">
|
||||
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
|
||||
<Button
|
||||
btnClasses={twMerge(
|
||||
$app.css?.['buttoncomponent']?.['button']?.class,
|
||||
fillContainer ? 'w-full h-full' : ''
|
||||
)}
|
||||
{disabled}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
on:click={async (e) => {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
appDrawer.toggleDrawer()
|
||||
}}
|
||||
{size}
|
||||
{color}
|
||||
>
|
||||
<div>{labelValue}</div>
|
||||
</Button>
|
||||
</AlignWrapper>
|
||||
</div>
|
||||
|
||||
<InputValue {id} input={configuration.gridContent} bind:value={gridContent} />
|
||||
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
|
||||
<InputValue {id} input={configuration.drawerTitle} bind:value={drawerTitle} />
|
||||
<InputValue {id} input={configuration.label} bind:value={labelValue} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue {id} input={configuration.fillContainer} bind:value={fillContainer} />
|
||||
|
||||
<Portal target="#app-editor-top-level-drawer">
|
||||
<Drawer bind:this={appDrawer} size="800px" alwaysOpen positionClass="!absolute">
|
||||
<DrawerContent
|
||||
title={drawerTitle}
|
||||
on:close={() => {
|
||||
appDrawer?.toggleDrawer()
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
{noPadding}
|
||||
{id}
|
||||
class={css?.container.class}
|
||||
style={css?.container.style}
|
||||
bind:subGrid={$app.subgrids[`${id}-0`]}
|
||||
containerHeight={1200}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</Portal>
|
||||
@@ -0,0 +1,73 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let componentContainerHeight: number
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let horizontal: boolean = false
|
||||
export let panes: number[]
|
||||
|
||||
export const staticOutputs: string[] = []
|
||||
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let noPadding: boolean | undefined = undefined
|
||||
|
||||
function onFocus() {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
}
|
||||
|
||||
$: $selectedComponent === id && onFocus()
|
||||
$: css = concatCustomCss($app.css?.containercomponent, customCss)
|
||||
|
||||
let sumedup = [50, 50]
|
||||
$: {
|
||||
let ns = panes.map((x) => (x / panes.reduce((a, b) => a + b, 0)) * 100)
|
||||
if (!deepEqual(ns, sumedup)) {
|
||||
sumedup = ns
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
|
||||
|
||||
<div class="h-full w-full border" on:pointerdown|stopPropagation>
|
||||
<Splitpanes {horizontal}>
|
||||
{#each sumedup as paneSize, index (index)}
|
||||
<Pane size={paneSize} minSize={20}>
|
||||
{#if $app.subgrids?.[`${id}-${index}`]}
|
||||
<SubGridEditor
|
||||
noYPadding
|
||||
{noPadding}
|
||||
{id}
|
||||
shouldHighlight={$focusedGrid?.subGridIndex === index}
|
||||
class={css?.container.class}
|
||||
style={css?.container.style}
|
||||
bind:subGrid={$app.subgrids[`${id}-${index}`]}
|
||||
containerHeight={horizontal
|
||||
? (componentContainerHeight * paneSize) / 100 - 1
|
||||
: componentContainerHeight - 8}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: index
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</Pane>
|
||||
{/each}
|
||||
</Splitpanes>
|
||||
</div>
|
||||
@@ -4,13 +4,17 @@
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, GridItem } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let componentContainerHeight: number
|
||||
export let tabs: string[]
|
||||
export let customCss:
|
||||
| ComponentCustomCSS<'tabRow' | 'tabs' | 'selectedTab' | 'container'>
|
||||
| undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['selectedTabIndex']
|
||||
const { app, worldStore, focusedGrid, selectedComponent } =
|
||||
@@ -29,6 +33,8 @@
|
||||
selectedTabIndex: Output<number | null>
|
||||
}
|
||||
|
||||
$: outputs?.selectedTabIndex && handleOutputs()
|
||||
|
||||
function handleTabSelection() {
|
||||
outputs?.selectedTabIndex.set(selectedIndex)
|
||||
if ($selectedComponent != id) {
|
||||
@@ -54,16 +60,27 @@
|
||||
}
|
||||
|
||||
$: $selectedComponent === id && selectedIndex >= 0 && onFocus()
|
||||
|
||||
$: css = concatCustomCss($app.css?.tabscomponent, customCss)
|
||||
|
||||
function handleOutputs() {
|
||||
outputs?.selectedTabIndex.set(outputs.selectedTabIndex.peak())
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.tabs} bind:value={tabs} />
|
||||
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
|
||||
|
||||
<div>
|
||||
<div bind:clientHeight={tabHeight} on:pointerdown|stopPropagation>
|
||||
<Tabs bind:selected>
|
||||
<Tabs bind:selected class={css?.tabRow?.class} style={css?.tabRow?.style}>
|
||||
{#each tabs ?? [] as res}
|
||||
<Tab value={res}>
|
||||
<Tab
|
||||
value={res}
|
||||
class={css?.tabs?.class}
|
||||
style={css?.tabs?.style}
|
||||
selectedClass={css?.selectedTab?.class}
|
||||
selectedStyle={css?.selectedTab?.style}
|
||||
>
|
||||
<span class="font-semibold">{res}</span>
|
||||
</Tab>
|
||||
{/each}
|
||||
@@ -72,9 +89,12 @@
|
||||
{#if $app.subgrids}
|
||||
{#each tabs ?? [] as res, i}
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={i === selectedIndex}
|
||||
bind:subGrid={$app.subgrids[`${id}-${i}`]}
|
||||
{noPadding}
|
||||
class={css?.container?.class}
|
||||
style={css?.container?.style}
|
||||
containerHeight={componentContainerHeight - tabHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export { default as AppContainer } from './AppContainer.svelte'
|
||||
export { default as AppDivider } from './AppDivider.svelte'
|
||||
export { default as AppTabs } from './AppTabs.svelte'
|
||||
export { default as AppDrawer } from './AppDrawer.svelte'
|
||||
export { default as AppSplitpanes } from './AppSplitpanes.svelte'
|
||||
export { default as AppTabs } from './AppTabs.svelte'
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import Icon from 'svelte-awesome'
|
||||
import { faCode, faPlus, faSliders } from '@fortawesome/free-solid-svg-icons'
|
||||
import ContextPanel from './contextPanel/ContextPanel.svelte'
|
||||
import { encodeState } from '$lib/utils'
|
||||
import { classNames, encodeState } from '$lib/utils'
|
||||
import AppPreview from './AppPreview.svelte'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
|
||||
@@ -59,14 +59,12 @@
|
||||
|
||||
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
|
||||
const errorByComponent = writable<Record<string, { error: string; componentId: string }>>({})
|
||||
|
||||
const focusedGrid = writable<FocusedGrid | undefined>(undefined)
|
||||
|
||||
setContext<AppEditorContext>('AppEditorContext', {
|
||||
worldStore,
|
||||
staticOutputs,
|
||||
app: appStore,
|
||||
lazyGrid: writable([]),
|
||||
summary: summaryStore,
|
||||
selectedComponent,
|
||||
mode,
|
||||
@@ -82,7 +80,9 @@
|
||||
noBackend: false,
|
||||
errorByComponent,
|
||||
openDebugRun: writable(undefined),
|
||||
focusedGrid
|
||||
focusedGrid,
|
||||
stateId: writable(0),
|
||||
parentWidth: writable(0)
|
||||
})
|
||||
|
||||
let timeout: NodeJS.Timeout | undefined = undefined
|
||||
@@ -126,7 +126,7 @@
|
||||
|
||||
{#if $connectingInput.opened}
|
||||
<div
|
||||
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
/>
|
||||
{/if}
|
||||
{#if !$userStore?.operator}
|
||||
@@ -138,7 +138,7 @@
|
||||
{#if previewing}
|
||||
<SplitPanesWrapper>
|
||||
<div
|
||||
class={twMerge('bg-gray-100 h-full w-full', $appStore.css?.['app']?.['viewer']?.class)}
|
||||
class={twMerge('h-full w-full', $appStore.css?.['app']?.['viewer']?.class)}
|
||||
style={$appStore.css?.['app']?.['viewer']?.style}
|
||||
>
|
||||
<AppPreview
|
||||
@@ -165,6 +165,10 @@
|
||||
<Splitpanes horizontal>
|
||||
<Pane size={$connectingInput?.opened ? 100 : 70}>
|
||||
<div
|
||||
on:pointerdown={(e) => {
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
class={twMerge(
|
||||
'bg-gray-100 h-full w-full',
|
||||
$appStore.css?.['app']?.['viewer']?.class
|
||||
@@ -172,14 +176,17 @@
|
||||
style={$appStore.css?.['app']?.['viewer']?.style}
|
||||
>
|
||||
<div
|
||||
class="relative mx-auto w-full h-full overflow-auto {app.fullscreen
|
||||
? ''
|
||||
: 'max-w-6xl'}"
|
||||
class={classNames(
|
||||
'relative mx-auto w-full h-full overflow-auto',
|
||||
app.fullscreen ? '' : 'max-w-6xl'
|
||||
)}
|
||||
>
|
||||
{#if $appStore.grid}
|
||||
<div class={width}>
|
||||
<div on:pointerdown|stopPropagation class={width}>
|
||||
<GridEditor {policy} />
|
||||
</div>
|
||||
|
||||
<div id="app-editor-top-level-drawer" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
</PanelSection>
|
||||
</Pane>
|
||||
<Pane size={75}>
|
||||
<div class="h-full w-full overflow-hidden">
|
||||
<div class="h-full w-full overflow-auto">
|
||||
{#if selectedJobId}
|
||||
{#if !job}
|
||||
<Skeleton layout={[[40]]} />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import AppComponentInput from './AppComponentInput.svelte'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
const { app, lazyGrid } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let resourceOnly: boolean = true
|
||||
</script>
|
||||
@@ -18,9 +18,9 @@
|
||||
<Toggle bind:checked={resourceOnly} options={{ right: 'Resource only' }} />
|
||||
</div>
|
||||
<div class="gap-4 flex flex-col pt-4">
|
||||
{#each $lazyGrid as gridItem (gridItem.data.id)}
|
||||
{#each $app.grid as gridItem (gridItem.data.id)}
|
||||
<div>
|
||||
{#if gridItem.data.type === 'tablecomponent'}
|
||||
{#if gridItem?.data?.type === 'tablecomponent'}
|
||||
<div>
|
||||
<AppComponentInput bind:component={gridItem.data} {resourceOnly} />
|
||||
<div class="ml-4 mt-4">
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
setContext<AppEditorContext>('AppEditorContext', {
|
||||
worldStore,
|
||||
staticOutputs,
|
||||
lazyGrid: writable(app.grid),
|
||||
app: appStore,
|
||||
summary: writable(summary),
|
||||
selectedComponent,
|
||||
@@ -62,7 +61,9 @@
|
||||
noBackend,
|
||||
errorByComponent: writable({}),
|
||||
openDebugRun: writable(undefined),
|
||||
focusedGrid: writable(undefined)
|
||||
focusedGrid: writable(undefined),
|
||||
stateId: writable(0),
|
||||
parentWidth: writable(0)
|
||||
})
|
||||
|
||||
let mounted = false
|
||||
@@ -76,6 +77,8 @@
|
||||
$: lockedClasses = isLocked ? '!max-h-[400px] overflow-hidden pointer-events-none' : ''
|
||||
</script>
|
||||
|
||||
<div id="app-editor-top-level-drawer" />
|
||||
|
||||
<div class="relative">
|
||||
<div
|
||||
class="{$$props.class} {lockedClasses} h-full
|
||||
@@ -87,6 +90,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if isLocked}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
connectingInput,
|
||||
staticOutputs,
|
||||
runnableComponents,
|
||||
lazyGrid,
|
||||
summary,
|
||||
focusedGrid
|
||||
focusedGrid,
|
||||
parentWidth
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
// The drag is disabled when the user is connecting an input
|
||||
@@ -36,7 +36,7 @@
|
||||
$app.grid.map((gridItem) => {
|
||||
const disabledGridItem = fct(gridItem)
|
||||
|
||||
if (disabledGridItem.data.subGrids) {
|
||||
if (disabledGridItem?.data?.subGrids) {
|
||||
disabledGridItem.data.subGrids = disabledGridItem.data.subGrids.map(
|
||||
(subgrid: GridItem[]) => subgrid?.map((subgridItem: GridItem) => fct(subgridItem)) ?? []
|
||||
)
|
||||
@@ -89,27 +89,16 @@
|
||||
}
|
||||
|
||||
function selectComponent(id: string) {
|
||||
// Component selection is handled manually in the Map component (pointerdown
|
||||
// event propagation is stopped to enable paning).
|
||||
// Update the 'selectComponent()' function as well when this is updated.
|
||||
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
$focusedGrid = undefined
|
||||
}
|
||||
}
|
||||
|
||||
$: $app.grid && recomputeLazyGrid()
|
||||
|
||||
let intervalId: NodeJS.Timeout | undefined = undefined
|
||||
function recomputeLazyGrid() {
|
||||
{
|
||||
if (intervalId) {
|
||||
clearTimeout(intervalId)
|
||||
}
|
||||
intervalId = setTimeout(() => {
|
||||
lazyGrid.set($app.grid)
|
||||
intervalId = undefined
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
let pointerdown = false
|
||||
const onpointerdown = () => {
|
||||
pointerdown = true
|
||||
@@ -135,9 +124,9 @@
|
||||
|
||||
<div class="relative w-full z-20 overflow-visible">
|
||||
<div
|
||||
class="w-full sticky top-0 flex justify-between border-l border-r border-b {$connectingInput?.opened
|
||||
class="w-full sticky top-0 flex justify-between border-b {$connectingInput?.opened
|
||||
? ''
|
||||
: 'bg-gray-50 '} px-4 py-2 items-center gap-4"
|
||||
: 'bg-gray-50 '} px-4 py-1 items-center gap-4"
|
||||
style="z-index: 1000;"
|
||||
>
|
||||
<h2 class="truncate">{$summary}</h2>
|
||||
@@ -149,9 +138,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
style={$app.css?.['app']?.['grid']?.style}
|
||||
class={twMerge('px-4 pt-4 pb-2 overflow-visible', $app.css?.['app']?.['grid']?.class ?? '')}
|
||||
class={twMerge('px-4 pt-4 pb-2 overflow-visible', $app.css?.['app']?.['grid']?.class ?? '')}
|
||||
on:pointerdown={onpointerdown}
|
||||
on:pointerleave={onpointerup}
|
||||
on:pointerup={onpointerup}
|
||||
@@ -159,59 +149,62 @@
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
bind:clientWidth={$parentWidth}
|
||||
>
|
||||
<Grid
|
||||
onTopId={$selectedComponent}
|
||||
fillSpace={false}
|
||||
bind:items={$app.grid}
|
||||
let:dataItem
|
||||
rowHeight={36}
|
||||
cols={columnConfiguration}
|
||||
fastStart={true}
|
||||
gap={[4, 2]}
|
||||
>
|
||||
{#each $lazyGrid as gridComponent (gridComponent.id)}
|
||||
{#if gridComponent.data.id === dataItem.data.id}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
{#if $connectingInput.opened}
|
||||
<div class={!$focusedGrid && $mode !== 'preview' ? 'border-gray-400 border border-dashed' : ''}>
|
||||
<Grid
|
||||
onTopId={$selectedComponent}
|
||||
fillSpace={false}
|
||||
bind:items={$app.grid}
|
||||
let:dataItem
|
||||
rowHeight={36}
|
||||
cols={columnConfiguration}
|
||||
fastStart={true}
|
||||
gap={[4, 2]}
|
||||
>
|
||||
{#each $app.grid as gridComponent (gridComponent.id)}
|
||||
{#if gridComponent?.data?.id && gridComponent?.data?.id === dataItem?.data?.id}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
{#if $connectingInput.opened}
|
||||
<div
|
||||
on:pointerenter={() => ($connectingInput.hoveredComponent = gridComponent.data.id)}
|
||||
on:pointerleave={() => ($connectingInput.hoveredComponent = undefined)}
|
||||
class="absolute w-full h-full bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
/>
|
||||
<div
|
||||
style="transform: translate(-50%, -50%);"
|
||||
class="absolute w-fit justify-center bg-indigo-500/90 left-[50%] top-[50%] z-50 px-6 rounded border text-white py-2 text-5xl center-center"
|
||||
>{dataItem.data.id}</div
|
||||
>
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:pointerenter={() => ($connectingInput.hoveredComponent = gridComponent.data.id)}
|
||||
on:pointerleave={() => ($connectingInput.hoveredComponent = undefined)}
|
||||
class="absolute w-full h-full bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
/>
|
||||
<div
|
||||
style="transform: translate(-50%, -50%);"
|
||||
class="absolute w-fit justify-center bg-indigo-500/90 left-[50%] top-[50%] z-50 px-6 rounded border text-white py-2 text-5xl center-center"
|
||||
>{dataItem.data.id}</div
|
||||
on:click|stopPropagation
|
||||
on:pointerdown={() => selectComponent(dataItem.data.id)}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
$selectedComponent === dataItem.data.id ? 'active-grid-item' : '',
|
||||
gridComponent.data.card ? 'border border-gray-100' : ''
|
||||
)}
|
||||
>
|
||||
<Component
|
||||
{pointerdown}
|
||||
component={gridComponent.data}
|
||||
selected={$selectedComponent === dataItem.data.id}
|
||||
locked={isFixed(gridComponent)}
|
||||
on:delete={() => removeGridElement(gridComponent.data)}
|
||||
on:lock={() => {
|
||||
let fComponent = $app.grid.find((c) => c.id === gridComponent.id)
|
||||
if (fComponent) {
|
||||
fComponent = toggleFixed(fComponent)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:click|stopPropagation
|
||||
on:pointerdown={() => selectComponent(dataItem.data.id)}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
$selectedComponent === dataItem.data.id ? 'active-grid-item' : '',
|
||||
gridComponent.data.card ? 'border border-gray-100' : ''
|
||||
)}
|
||||
>
|
||||
<Component
|
||||
{pointerdown}
|
||||
bind:pComponent={gridComponent.data}
|
||||
selected={$selectedComponent === dataItem.data.id}
|
||||
locked={isFixed(gridComponent)}
|
||||
on:delete={() => removeGridElement(gridComponent.data)}
|
||||
on:lock={() => {
|
||||
let fComponent = $app.grid.find((c) => c.id === gridComponent.id)
|
||||
if (fComponent) {
|
||||
fComponent = toggleFixed(fComponent)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</Grid>
|
||||
{/each}
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -222,7 +215,7 @@
|
||||
id={`bg_${index}`}
|
||||
inlineScript={script.inlineScript}
|
||||
name={script.name}
|
||||
bind:fields={script.fields}
|
||||
fields={script.fields}
|
||||
bind:staticOutputs={$staticOutputs[`bg_${index}`]}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
32
frontend/src/lib/components/apps/editor/GridPanel.svelte
Normal file
32
frontend/src/lib/components/apps/editor/GridPanel.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, GridItem } from '../types'
|
||||
import ComponentPanel from './settingsPanel/ComponentPanel.svelte'
|
||||
|
||||
const { selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
export let gridItems: GridItem[]
|
||||
export let parent: string | undefined
|
||||
</script>
|
||||
|
||||
{#each gridItems as gridItem, i (gridItem?.data?.id ?? i)}
|
||||
{#if gridItem?.data?.id === $selectedComponent}
|
||||
<ComponentPanel {parent} bind:component={gridItem.data} />
|
||||
{:else if gridItem?.data?.type === 'tablecomponent'}
|
||||
{#each gridItem.data.actionButtons as actionButton (actionButton.id)}
|
||||
{#if actionButton.id === $selectedComponent}
|
||||
<ComponentPanel
|
||||
parent={undefined}
|
||||
noGrid
|
||||
rowColumns
|
||||
bind:component={actionButton}
|
||||
duplicateMoveAllowed={false}
|
||||
onDelete={() => {
|
||||
gridItem.data.actionButtons = gridItem.data.actionButtons.filter(
|
||||
(c) => c.id !== actionButton.id
|
||||
)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -1,44 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import { allItems, allItemsWithParent } from '../utils'
|
||||
import GridPanel from './GridPanel.svelte'
|
||||
import PanelSection from './settingsPanel/common/PanelSection.svelte'
|
||||
import ComponentPanel from './settingsPanel/ComponentPanel.svelte'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
import TablePanel from './TablePanel.svelte'
|
||||
|
||||
const { selectedComponent, app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { selectedComponent, app, stateId } = getContext<AppEditorContext>('AppEditorContext')
|
||||
</script>
|
||||
|
||||
{#each $app.grid as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.id === $selectedComponent}
|
||||
<ComponentPanel parent={undefined} bind:component={gridItem.data} />
|
||||
{:else if gridItem.data.type === 'tablecomponent'}
|
||||
<TablePanel bind:component={gridItem.data} />
|
||||
{/if}
|
||||
{/each}
|
||||
{#if $app.grid}
|
||||
<GridPanel parent={undefined} bind:gridItems={$app.grid} />
|
||||
{/if}
|
||||
|
||||
{#if $app.subgrids}
|
||||
{#each Object.keys($app.subgrids ?? {}) as key (key)}
|
||||
{#each $app.subgrids[key] as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.id === $selectedComponent}
|
||||
<ComponentPanel parent={key} bind:component={gridItem.data} />
|
||||
{:else if gridItem.data.type === 'tablecomponent'}
|
||||
<TablePanel bind:component={gridItem.data} />
|
||||
{/if}
|
||||
{/each}
|
||||
<GridPanel parent={key} bind:gridItems={$app.subgrids[key]} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#each $app?.hiddenInlineScripts ?? [] as script, index (script.name)}
|
||||
{#if $selectedComponent === `bg_${index}`}
|
||||
<PanelSection title={`Background script inputs`}>
|
||||
<InputsSpecsEditor
|
||||
id={`bg_${index}`}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={script.fields}
|
||||
userInputEnabled={false}
|
||||
/>
|
||||
{#key $stateId}
|
||||
<InputsSpecsEditor
|
||||
id={`bg_${index}`}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={script.fields}
|
||||
userInputEnabled={false}
|
||||
/>
|
||||
{/key}
|
||||
</PanelSection>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -13,17 +13,20 @@
|
||||
export { classes as class }
|
||||
export let style = ''
|
||||
export let noPadding = false
|
||||
//export let id: string
|
||||
export let noYPadding = false
|
||||
export let subGrid: GridItem[] = []
|
||||
export let visible: boolean = true
|
||||
export let id: string
|
||||
export let shouldHighlight: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const { app, connectingInput, selectedComponent, focusedGrid } =
|
||||
const { app, connectingInput, selectedComponent, focusedGrid, mode, parentWidth } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let pointerdown = false
|
||||
$: highlight = id === $focusedGrid?.parentComponentId && shouldHighlight
|
||||
|
||||
let pointerdown = false
|
||||
let onComponent: string | undefined = undefined
|
||||
|
||||
const onpointerdown = (e) => {
|
||||
@@ -64,17 +67,22 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="relative w-full subgrid {visible ? 'visible' : 'invisible h-0 overflow-hidden'}"
|
||||
class="relative w-full subgrid {visible ? 'visible' : 'invisible h-0 overflow-hidden'} "
|
||||
bind:this={container}
|
||||
>
|
||||
<div
|
||||
class={twMerge('py-2 overflow-auto', classes ?? '', noPadding ? 'px-0' : 'px-2')}
|
||||
class={twMerge(
|
||||
'overflow-auto',
|
||||
noYPadding ? '' : 'py-2',
|
||||
classes ?? '',
|
||||
noPadding ? 'px-0' : 'px-2'
|
||||
)}
|
||||
on:pointerdown|stopPropagation={onpointerdown}
|
||||
on:pointerleave={onpointerup}
|
||||
on:pointerup={onpointerup}
|
||||
style="height: {containerHeight}px; {style ?? ''}"
|
||||
>
|
||||
<div>
|
||||
<div class={highlight && $mode !== 'preview' ? 'border-gray-400 border border-dashed' : ''}>
|
||||
<Grid
|
||||
bind:items={subGrid}
|
||||
let:dataItem
|
||||
@@ -83,19 +91,17 @@
|
||||
fastStart={true}
|
||||
gap={[4, 2]}
|
||||
scroller={container}
|
||||
parentWidth={$parentWidth - 17}
|
||||
>
|
||||
{#each subGrid as gridComponent (gridComponent.id)}
|
||||
{#if gridComponent.data.id === dataItem.data.id}
|
||||
{#if gridComponent?.data?.id && gridComponent?.data?.id === dataItem?.data?.id}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
{#if $connectingInput.opened}
|
||||
{#if $connectingInput.opened}
|
||||
<div
|
||||
on:pointerenter={() =>
|
||||
($connectingInput.hoveredComponent = gridComponent.data.id)}
|
||||
on:pointerleave={() => ($connectingInput.hoveredComponent = undefined)}
|
||||
class="absolute w-full h-full bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
/>
|
||||
{/if}
|
||||
<div
|
||||
on:pointerenter={() => ($connectingInput.hoveredComponent = gridComponent.data.id)}
|
||||
on:pointerleave={() => ($connectingInput.hoveredComponent = undefined)}
|
||||
class="absolute w-full h-full bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
/>
|
||||
<div
|
||||
style="transform: translate(-50%, -50%);"
|
||||
class="absolute w-fit justify-center bg-indigo-500/90 left-[50%] top-[50%] z-50 px-6 rounded border text-white py-2 text-5xl center-center"
|
||||
@@ -116,7 +122,7 @@
|
||||
>
|
||||
<Component
|
||||
{pointerdown}
|
||||
bind:pComponent={gridComponent.data}
|
||||
component={gridComponent.data}
|
||||
selected={$selectedComponent === dataItem.data.id}
|
||||
locked={isFixed(gridComponent)}
|
||||
on:lock={() => lock(gridComponent)}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import type { TableComponent } from './component'
|
||||
import ComponentPanel from './settingsPanel/ComponentPanel.svelte'
|
||||
|
||||
export let component: TableComponent
|
||||
const { selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
</script>
|
||||
|
||||
{#each component.actionButtons as actionButton (actionButton.id)}
|
||||
{#if actionButton.id === $selectedComponent}
|
||||
<ComponentPanel
|
||||
parent={undefined}
|
||||
noGrid
|
||||
rowColumns
|
||||
bind:component={actionButton}
|
||||
onDelete={() => {
|
||||
component.actionButtons = component.actionButtons.filter((c) => c.id !== actionButton.id)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -5,8 +5,7 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import ComponentHeader from '../ComponentHeader.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
import type { AppComponent } from './components'
|
||||
import {
|
||||
AppBarChart,
|
||||
AppDisplayComponent,
|
||||
@@ -34,12 +33,15 @@
|
||||
AppDivider,
|
||||
AppFileInput,
|
||||
AppImage,
|
||||
AppContainer
|
||||
AppContainer,
|
||||
AppAggridTable,
|
||||
AppDrawer,
|
||||
AppMap,
|
||||
AppSplitpanes
|
||||
} from '../../components'
|
||||
import type { AppComponent } from './components'
|
||||
import AppAggridTable from '../../components/display/table/AppAggridTable.svelte'
|
||||
import AppMultiSelect from '../../components/inputs/AppMultiSelect.svelte'
|
||||
|
||||
export let pComponent: AppComponent
|
||||
export let component: AppComponent
|
||||
export let selected: boolean
|
||||
export let locked: boolean = false
|
||||
export let pointerdown: boolean = false
|
||||
@@ -50,10 +52,10 @@
|
||||
let initializing: boolean | undefined = undefined
|
||||
let componentContainerHeight: number = 0
|
||||
|
||||
let component = JSON.parse(JSON.stringify(pComponent))
|
||||
$: if (pComponent && !deepEqual(pComponent, component)) {
|
||||
component = JSON.parse(JSON.stringify(pComponent))
|
||||
}
|
||||
// let component = JSON.parse(JSON.stringify({ conf }))
|
||||
// $: if (!deepEqual(component, pComponent)) {
|
||||
// component = JSON.parse(JSON.stringify(pComponent))
|
||||
// }
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -89,148 +91,302 @@
|
||||
>
|
||||
{#if component.type === 'displaycomponent'}
|
||||
<AppDisplayComponent
|
||||
{...component}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'barchartcomponent'}
|
||||
<AppBarChart
|
||||
{...component}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'timeseriescomponent'}
|
||||
<AppTimeseries
|
||||
{...component}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'htmlcomponent'}
|
||||
<AppHtml
|
||||
{...component}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'vegalitecomponent'}
|
||||
<VegaLiteHtml
|
||||
{...component}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
bind:initializing
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'plotlycomponent'}
|
||||
<PlotlyHtml
|
||||
{...component}
|
||||
id={component.id}
|
||||
bind:initializing
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'scatterchartcomponent'}
|
||||
<AppScatterChart
|
||||
{...component}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'piechartcomponent'}
|
||||
<AppPieChart
|
||||
{...component}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
/>
|
||||
{:else if component.type === 'tablecomponent'}
|
||||
<AppTable
|
||||
{...component}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:actionButtons={component.actionButtons}
|
||||
/>
|
||||
{:else if component.type === 'aggridcomponent'}
|
||||
<AppAggridTable
|
||||
{...component}
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
/>
|
||||
{:else if component.type === 'textcomponent'}
|
||||
<AppText
|
||||
{...component}
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:componentInput={component.componentInput}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'buttoncomponent'}
|
||||
<AppButton
|
||||
{...component}
|
||||
bind:componentInput={component.componentInput}
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'selectcomponent'}
|
||||
<AppSelect {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppSelect
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'multiselectcomponent'}
|
||||
<AppMultiSelect
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'formcomponent'}
|
||||
<AppForm
|
||||
{...component}
|
||||
bind:componentInput={component.componentInput}
|
||||
id={component.id}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'formbuttoncomponent'}
|
||||
<AppFormButton
|
||||
{...component}
|
||||
bind:componentInput={component.componentInput}
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'checkboxcomponent'}
|
||||
<AppCheckbox {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppCheckbox
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'textinputcomponent'}
|
||||
<AppTextInput {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppTextInput
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'passwordinputcomponent'}
|
||||
<AppTextInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="password"
|
||||
{...component}
|
||||
appCssKey="passwordinputcomponent"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'dateinputcomponent'}
|
||||
<AppDateInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="date"
|
||||
{...component}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'numberinputcomponent'}
|
||||
<AppNumberInput {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppNumberInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'currencycomponent'}
|
||||
<AppCurrencyInput {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppCurrencyInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'slidercomponent'}
|
||||
<AppSliderInputs {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppSliderInputs
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'horizontaldividercomponent'}
|
||||
<AppDivider {...component} position="horizontal" />
|
||||
<AppDivider
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
position="horizontal"
|
||||
/>
|
||||
{:else if component.type === 'verticaldividercomponent'}
|
||||
<AppDivider {...component} position="vertical" />
|
||||
<AppDivider
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
position="vertical"
|
||||
/>
|
||||
{:else if component.type === 'rangecomponent'}
|
||||
<AppRangeInput {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppRangeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'tabscomponent'}
|
||||
<AppTabs
|
||||
{...component}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
tabs={component.tabs}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{componentContainerHeight}
|
||||
/>
|
||||
{:else if component.type === 'containercomponent'}
|
||||
<AppContainer
|
||||
{...component}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{componentContainerHeight}
|
||||
/>
|
||||
{:else if component.type === 'verticalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{componentContainerHeight}
|
||||
/>
|
||||
{:else if component.type === 'horizontalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{componentContainerHeight}
|
||||
horizontal={true}
|
||||
/>
|
||||
{:else if component.type === 'iconcomponent'}
|
||||
<AppIcon {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppIcon
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'fileinputcomponent'}
|
||||
<AppFileInput {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppFileInput
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'imagecomponent'}
|
||||
<AppImage {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
<AppImage
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'drawercomponent'}
|
||||
<AppDrawer
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
/>
|
||||
{:else if component.type === 'mapcomponent'}
|
||||
<AppMap {...component} bind:staticOutputs={$staticOutputs[component.id]} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,11 @@ import {
|
||||
SeparatorHorizontal,
|
||||
SeparatorVertical,
|
||||
Paperclip,
|
||||
Image
|
||||
Image,
|
||||
SidebarClose,
|
||||
MapPin,
|
||||
FlipHorizontal,
|
||||
FlipVertical
|
||||
} from 'lucide-svelte'
|
||||
import type { BaseAppComponent } from '../../types'
|
||||
|
||||
@@ -68,6 +72,7 @@ export type DisplayComponent = BaseComponent<'displaycomponent'>
|
||||
export type ImageComponent = BaseComponent<'imagecomponent'>
|
||||
export type InputComponent = BaseComponent<'inputcomponent'>
|
||||
export type SelectComponent = BaseComponent<'selectcomponent'>
|
||||
export type MultiSelectComponent = BaseComponent<'multiselectcomponent'>
|
||||
export type CheckboxComponent = BaseComponent<'checkboxcomponent'>
|
||||
export type RadioComponent = BaseComponent<'radiocomponent'>
|
||||
export type IconComponent = BaseComponent<'iconcomponent'>
|
||||
@@ -78,6 +83,14 @@ export type TabsComponent = BaseComponent<'tabscomponent'> & {
|
||||
tabs: string[]
|
||||
}
|
||||
export type ContainerComponent = BaseComponent<'containercomponent'>
|
||||
export type DrawerComponent = BaseComponent<'drawercomponent'>
|
||||
export type MapComponent = BaseComponent<'mapcomponent'>
|
||||
export type VerticalSplitPanesComponent = BaseComponent<'verticalsplitpanescomponent'> & {
|
||||
panes: number[]
|
||||
}
|
||||
export type HorizontalSplitPanesComponent = BaseComponent<'horizontalsplitpanescomponent'> & {
|
||||
panes: number[]
|
||||
}
|
||||
|
||||
export type AppComponent = BaseAppComponent &
|
||||
(
|
||||
@@ -94,11 +107,11 @@ export type AppComponent = BaseAppComponent &
|
||||
| HtmlComponent
|
||||
| TableComponent
|
||||
| TextComponent
|
||||
| TableComponent
|
||||
| ButtonComponent
|
||||
| PieChartComponent
|
||||
| ScatterChartComponent
|
||||
| SelectComponent
|
||||
| MultiSelectComponent
|
||||
| CheckboxComponent
|
||||
| FormComponent
|
||||
| FormButtonComponent
|
||||
@@ -112,6 +125,10 @@ export type AppComponent = BaseAppComponent &
|
||||
| FileInputComponent
|
||||
| ImageComponent
|
||||
| AggridComponent
|
||||
| DrawerComponent
|
||||
| MapComponent
|
||||
| VerticalSplitPanesComponent
|
||||
| HorizontalSplitPanesComponent
|
||||
)
|
||||
|
||||
export type AppComponentDimensions = `${IntRange<
|
||||
@@ -272,6 +289,12 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
|
||||
type: 'static',
|
||||
value: ''
|
||||
},
|
||||
gotoNewTab: {
|
||||
tooltip: 'Go to create a new tab',
|
||||
fieldType: 'boolean',
|
||||
type: 'static',
|
||||
value: true
|
||||
},
|
||||
beforeIcon: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
@@ -850,7 +873,7 @@ Hello \${ctx.username}
|
||||
{ value: 'bar', label: 'Bar' }
|
||||
]
|
||||
},
|
||||
multiple: {
|
||||
create: {
|
||||
type: 'static',
|
||||
fieldType: 'boolean',
|
||||
value: false,
|
||||
@@ -861,6 +884,43 @@ Hello \${ctx.username}
|
||||
fieldType: 'text',
|
||||
value: 'Select an item',
|
||||
onlyStatic: true
|
||||
},
|
||||
defaultValue: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'object'
|
||||
}
|
||||
},
|
||||
customCss: {
|
||||
input: { style: '' }
|
||||
} as const,
|
||||
card: false,
|
||||
softWrap: true
|
||||
}
|
||||
},
|
||||
multiselectcomponent: {
|
||||
name: 'MultiSelect',
|
||||
icon: List,
|
||||
dims: '2:1-3:1',
|
||||
data: {
|
||||
verticalAlignment: 'center',
|
||||
id: '',
|
||||
type: 'multiselectcomponent',
|
||||
componentInput: undefined,
|
||||
configuration: {
|
||||
items: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'text',
|
||||
value: [
|
||||
"Foo", "Bar"
|
||||
]
|
||||
},
|
||||
placeholder: {
|
||||
type: 'static',
|
||||
fieldType: 'text',
|
||||
value: 'Select items',
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {
|
||||
@@ -908,7 +968,9 @@ Hello \${ctx.username}
|
||||
fieldType: 'number'
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
input: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -949,7 +1011,9 @@ Hello \${ctx.username}
|
||||
optionValuesKey: 'localeOptions'
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
input: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -986,7 +1050,11 @@ Hello \${ctx.username}
|
||||
fieldType: 'number'
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
handle: { style: '' },
|
||||
limits: { class: '', style: '' },
|
||||
value: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -1027,7 +1095,11 @@ Hello \${ctx.username}
|
||||
fieldType: 'number'
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
handles: { style: '' },
|
||||
limits: { class: '', style: '' },
|
||||
values: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -1049,7 +1121,9 @@ Hello \${ctx.username}
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
input: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -1080,7 +1154,9 @@ Hello \${ctx.username}
|
||||
fieldType: 'date'
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
input: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -1100,6 +1176,12 @@ Hello \${ctx.username}
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {
|
||||
tabRow: { class: '', style: '' },
|
||||
allTabs: { class: '', style: '' },
|
||||
selectedTab: { class: '', style: '' },
|
||||
container: { class: '', style: '' }
|
||||
} as const,
|
||||
componentInput: undefined,
|
||||
card: false,
|
||||
numberOfSubgrids: 2,
|
||||
@@ -1141,7 +1223,10 @@ Hello \${ctx.username}
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
container: { class: '', style: '' },
|
||||
icon: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -1168,7 +1253,10 @@ Hello \${ctx.username}
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
container: { class: '', style: '' },
|
||||
divider: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -1195,7 +1283,10 @@ Hello \${ctx.username}
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
container: { class: '', style: '' },
|
||||
divider: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -1228,7 +1319,9 @@ Hello \${ctx.username}
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
customCss: {
|
||||
container: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
@@ -1247,7 +1340,7 @@ Hello \${ctx.username}
|
||||
fieldType: 'text',
|
||||
fileUpload: {
|
||||
accept: 'image/*',
|
||||
base64: true
|
||||
convertTo: 'base64'
|
||||
}
|
||||
},
|
||||
imageFit: {
|
||||
@@ -1263,16 +1356,177 @@ Hello \${ctx.username}
|
||||
fieldType: 'text',
|
||||
onlyStatic: true,
|
||||
tooltip: "This text will appear if the image can't be loaded for any reason"
|
||||
},
|
||||
customStyles: {
|
||||
}
|
||||
},
|
||||
customCss: {
|
||||
image: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
drawercomponent: {
|
||||
name: 'Drawer',
|
||||
icon: SidebarClose,
|
||||
dims: '1:1-2:1',
|
||||
data: {
|
||||
softWrap: true,
|
||||
id: '',
|
||||
type: 'drawercomponent',
|
||||
horizontalAlignment: 'center',
|
||||
verticalAlignment: 'center',
|
||||
configuration: {
|
||||
noPadding: {
|
||||
type: 'static',
|
||||
value: '',
|
||||
fieldType: 'textarea',
|
||||
fieldType: 'boolean',
|
||||
value: false,
|
||||
onlyStatic: true
|
||||
},
|
||||
drawerTitle: {
|
||||
type: 'static',
|
||||
fieldType: 'text',
|
||||
value: 'Drawer title',
|
||||
onlyStatic: true
|
||||
},
|
||||
label: {
|
||||
type: 'static',
|
||||
fieldType: 'text',
|
||||
value: 'Press me'
|
||||
},
|
||||
color: {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'buttonColorOptions',
|
||||
value: 'blue'
|
||||
},
|
||||
size: {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'buttonSizeOptions',
|
||||
value: 'xs'
|
||||
},
|
||||
fillContainer: {
|
||||
fieldType: 'boolean',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
value: false
|
||||
},
|
||||
disabled: {
|
||||
fieldType: 'boolean',
|
||||
type: 'eval',
|
||||
expr: 'false'
|
||||
}
|
||||
},
|
||||
componentInput: undefined,
|
||||
card: false,
|
||||
numberOfSubgrids: 1
|
||||
}
|
||||
},
|
||||
mapcomponent: {
|
||||
name: 'Map',
|
||||
icon: MapPin,
|
||||
dims: '3:6-6:10',
|
||||
data: {
|
||||
id: '',
|
||||
type: 'mapcomponent',
|
||||
componentInput: undefined,
|
||||
configuration: {
|
||||
longitude: {
|
||||
fieldType: 'number',
|
||||
type: 'static',
|
||||
value: 15
|
||||
},
|
||||
latitude: {
|
||||
fieldType: 'number',
|
||||
type: 'static',
|
||||
value: 50
|
||||
},
|
||||
zoom: {
|
||||
fieldType: 'number',
|
||||
type: 'static',
|
||||
value: 3
|
||||
},
|
||||
markers: {
|
||||
fieldType: 'array',
|
||||
type: 'static',
|
||||
subFieldType: 'object',
|
||||
fileUpload: {
|
||||
accept: 'application/json',
|
||||
convertTo: 'text'
|
||||
},
|
||||
value: [
|
||||
{
|
||||
lon: 12.496366,
|
||||
lat: 41.902783,
|
||||
title: 'Rome',
|
||||
radius: 7,
|
||||
color: '#dc2626',
|
||||
strokeWidth: 3,
|
||||
strokeColor: '#fca5a5'
|
||||
},
|
||||
{
|
||||
lon: -0.136439,
|
||||
lat: 51.507359,
|
||||
title: 'London'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
customCss: {
|
||||
map: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
verticalsplitpanescomponent: {
|
||||
name: 'Vertical Split Panes',
|
||||
icon: FlipHorizontal,
|
||||
dims: '2:8-6:8',
|
||||
data: {
|
||||
softWrap: true,
|
||||
id: '',
|
||||
type: 'verticalsplitpanescomponent',
|
||||
configuration: {
|
||||
noPadding: {
|
||||
type: 'static',
|
||||
fieldType: 'boolean',
|
||||
value: false,
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
card: false
|
||||
customCss: {
|
||||
container: { class: '', style: '' }
|
||||
} as const,
|
||||
componentInput: undefined,
|
||||
card: false,
|
||||
panes: [50, 50],
|
||||
numberOfSubgrids: 2
|
||||
}
|
||||
},
|
||||
horizontalsplitpanescomponent: {
|
||||
name: 'Horizontal Split Panes',
|
||||
icon: FlipVertical,
|
||||
dims: '2:8-6:8',
|
||||
data: {
|
||||
softWrap: true,
|
||||
id: '',
|
||||
type: 'horizontalsplitpanescomponent',
|
||||
configuration: {
|
||||
noPadding: {
|
||||
type: 'static',
|
||||
fieldType: 'boolean',
|
||||
value: false,
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {
|
||||
container: { class: '', style: '' }
|
||||
} as const,
|
||||
componentInput: undefined,
|
||||
card: false,
|
||||
panes: [50, 50],
|
||||
numberOfSubgrids: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@ const layout: ComponentSet = {
|
||||
'tabscomponent',
|
||||
'containercomponent',
|
||||
'horizontaldividercomponent',
|
||||
'verticaldividercomponent'
|
||||
'verticaldividercomponent',
|
||||
'drawercomponent',
|
||||
'verticalsplitpanescomponent',
|
||||
'horizontalsplitpanescomponent'
|
||||
]
|
||||
} as const
|
||||
|
||||
@@ -27,7 +30,8 @@ const inputs: ComponentSet = {
|
||||
'dateinputcomponent',
|
||||
'fileinputcomponent',
|
||||
'checkboxcomponent',
|
||||
'selectcomponent'
|
||||
'selectcomponent',
|
||||
'multiselectcomponent',
|
||||
]
|
||||
} as const
|
||||
|
||||
@@ -37,6 +41,7 @@ const display: ComponentSet = {
|
||||
'textcomponent',
|
||||
'iconcomponent',
|
||||
'imagecomponent',
|
||||
'mapcomponent',
|
||||
'htmlcomponent',
|
||||
'tablecomponent',
|
||||
'aggridcomponent',
|
||||
@@ -46,8 +51,8 @@ const display: ComponentSet = {
|
||||
'plotlycomponent',
|
||||
'scatterchartcomponent',
|
||||
'timeseriescomponent',
|
||||
'displaycomponent',
|
||||
'displaycomponent'
|
||||
]
|
||||
} as const
|
||||
|
||||
export const COMPONENT_SETS = [layout, buttons, inputs, display] as const
|
||||
export const COMPONENT_SETS = [layout, buttons, inputs, display] as const
|
||||
|
||||
@@ -178,7 +178,6 @@
|
||||
{#each ids as id}
|
||||
<div class="mb-3">
|
||||
{#if $app?.css?.[type][id]}
|
||||
{JSON.stringify($app.css[type][id])}
|
||||
<CssProperty
|
||||
name={id}
|
||||
bind:value={$app.css[type][id]}
|
||||
|
||||
@@ -9,6 +9,18 @@ export const staticValues = {
|
||||
chartThemeOptions: ['theme1', 'theme2', 'theme3'],
|
||||
textStyleOptions: ['Title', 'Subtitle', 'Body', 'Label', 'Caption'],
|
||||
currencyOptions: ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'CNY', 'INR', 'BRL'],
|
||||
localeOptions: ['en-US', 'en-GB', 'en-IE', 'de-DE', 'fr-FR', 'br-FR', 'ja-JP', 'pt-TL', 'fr-CA', 'en-CA'],
|
||||
objectFitOptions: ['contain', 'cover', 'fill']
|
||||
localeOptions: [
|
||||
'en-US',
|
||||
'en-GB',
|
||||
'en-IE',
|
||||
'de-DE',
|
||||
'fr-FR',
|
||||
'br-FR',
|
||||
'ja-JP',
|
||||
'pt-TL',
|
||||
'fr-CA',
|
||||
'en-CA'
|
||||
],
|
||||
objectFitOptions: ['contain', 'cover', 'fill'],
|
||||
splitPanelOptions: ['2', '3', '4']
|
||||
} as const
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
function getComponentNameById(componentId: string) {
|
||||
const component = findGridItem($app, componentId)
|
||||
|
||||
if (component?.data.type) {
|
||||
if (component?.data?.type) {
|
||||
return components[component?.data.type].name
|
||||
} else if (componentId == 'ctx') {
|
||||
return 'Context'
|
||||
@@ -50,6 +50,7 @@
|
||||
>
|
||||
{#each panels as [componentId, outputs] (componentId)}
|
||||
{#if outputs.length > 0 && $worldStore?.outputsById[componentId]}
|
||||
{@const name = getComponentNameById(componentId)}
|
||||
<div>
|
||||
<div
|
||||
class="flex {$connectingInput?.opened
|
||||
@@ -75,7 +76,7 @@
|
||||
'bg-gray-500 text-white'
|
||||
)}
|
||||
>
|
||||
{getComponentNameById(componentId)}
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -92,7 +93,9 @@
|
||||
{#key $connectingInput?.opened}
|
||||
<ComponentOutputViewer
|
||||
outputs={$connectingInput?.opened && $selectedComponent === componentId
|
||||
? ['search']
|
||||
? name == 'Table'
|
||||
? ['search']
|
||||
: []
|
||||
: outputs}
|
||||
{componentId}
|
||||
on:select={({ detail }) => {
|
||||
|
||||
@@ -13,14 +13,20 @@
|
||||
import { emptySchema, scriptLangToEditorLang } from '$lib/utils'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import Popover from '../../../Popover.svelte'
|
||||
import { computeFields } from './utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import type { AppInput } from '../../inputType'
|
||||
|
||||
let inlineScriptEditorDrawer: InlineScriptEditorDrawer
|
||||
|
||||
export let inlineScript: InlineScript
|
||||
export let name: string | undefined = undefined
|
||||
export let id: string
|
||||
export let defaultUserInput: boolean = false
|
||||
export let fields: Record<string, AppInput> = {}
|
||||
export let syncFields: boolean = false
|
||||
|
||||
const { runnableComponents } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { runnableComponents, stateId } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let editor: Editor
|
||||
let validCode = true
|
||||
@@ -49,9 +55,24 @@
|
||||
emptySchema()
|
||||
)
|
||||
}
|
||||
if (inlineScript.schema) {
|
||||
loadSchemaAndInputsByName()
|
||||
}
|
||||
})
|
||||
const dispatch = createEventDispatcher()
|
||||
let runLoading = false
|
||||
|
||||
async function loadSchemaAndInputsByName() {
|
||||
if (syncFields) {
|
||||
const newSchema = inlineScript.schema
|
||||
const newFields = computeFields(newSchema, defaultUserInput, fields)
|
||||
|
||||
if (!deepEqual(newFields, fields)) {
|
||||
fields = newFields
|
||||
$stateId++
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<InlineScriptEditorDrawer {editor} bind:this={inlineScriptEditorDrawer} bind:inlineScript />
|
||||
@@ -151,6 +172,7 @@
|
||||
await inferInlineScriptSchema(inlineScript?.language, e.detail, inlineScript.schema)
|
||||
if (JSON.stringify(inlineScript.schema) != oldSchema) {
|
||||
inlineScript = inlineScript
|
||||
loadSchemaAndInputsByName()
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
export let componentInput: AppInput | undefined
|
||||
export let defaultUserInput = false
|
||||
export let id: string
|
||||
|
||||
async function fork(path: string) {
|
||||
@@ -73,9 +74,12 @@
|
||||
{#if componentInput?.runnable?.type === 'runnableByName' && componentInput?.runnable?.name !== undefined}
|
||||
{#if componentInput.runnable.inlineScript}
|
||||
<InlineScriptEditor
|
||||
{defaultUserInput}
|
||||
{id}
|
||||
bind:inlineScript={componentInput.runnable.inlineScript}
|
||||
bind:name={componentInput.runnable.name}
|
||||
bind:fields={componentInput.fields}
|
||||
syncFields
|
||||
on:delete={() => {
|
||||
if (componentInput && componentInput.type == 'runnable') {
|
||||
componentInput = clearResultAppInput(componentInput)
|
||||
|
||||
@@ -21,18 +21,22 @@
|
||||
</Pane>
|
||||
<Pane size={75}>
|
||||
{#if !selectedScriptComponentId}
|
||||
<span class="p-2 text-sm text-gray-600">Select a script on the left panel</span>
|
||||
<div class="text-sm text-gray-500 text-center py-8 px-2">
|
||||
Select a script on the left panel
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#each $app.grid as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.id === selectedScriptComponentId}
|
||||
{#each $app.grid as gridItem, index (gridItem?.data?.id ?? index)}
|
||||
{#if gridItem?.data?.id && gridItem.data.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
defaultUserInput={gridItem?.data?.type == 'formcomponent' ||
|
||||
gridItem?.data?.type == 'buttonformcomponent'}
|
||||
id={gridItem.data.id}
|
||||
bind:componentInput={gridItem.data.componentInput}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if gridItem.data.type === 'tablecomponent'}
|
||||
{#if gridItem?.data?.type === 'tablecomponent'}
|
||||
{#each gridItem.data.actionButtons as actionButton, index (index)}
|
||||
{#if actionButton.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
@@ -46,15 +50,17 @@
|
||||
|
||||
{#if $app.subgrids}
|
||||
{#each Object.keys($app.subgrids ?? {}) as key (key)}
|
||||
{#each $app.subgrids[key] as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.id === selectedScriptComponentId}
|
||||
{#each $app.subgrids[key] as gridItem (gridItem?.data?.id)}
|
||||
{#if gridItem?.data?.id && gridItem.data.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
defaultUserInput={gridItem.data?.type == 'formcomponent' ||
|
||||
gridItem.data?.type == 'buttonformcomponent'}
|
||||
id={gridItem.data.id}
|
||||
bind:componentInput={gridItem.data.componentInput}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if gridItem.data.type === 'tablecomponent'}
|
||||
{#if gridItem?.data?.type === 'tablecomponent'}
|
||||
{#each gridItem.data.actionButtons as actionButton, index (index)}
|
||||
{#if actionButton.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
@@ -89,6 +95,8 @@
|
||||
id={`bg_${index}`}
|
||||
bind:inlineScript={hiddenInlineScript.inlineScript}
|
||||
bind:name={hiddenInlineScript.name}
|
||||
bind:fields={hiddenInlineScript.fields}
|
||||
syncFields
|
||||
on:delete={() => {
|
||||
// remove the script from the array at the index
|
||||
$app.hiddenInlineScripts.splice(index, 1)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
const PREFIX = 'script-selector-' as const
|
||||
|
||||
export let selectedScriptComponentId: string | undefined = undefined
|
||||
const { app, selectedComponent, lazyGrid } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let list: HTMLElement
|
||||
|
||||
function selectScript(id: string) {
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: runnables = getAppScripts($lazyGrid, $app.subgrids)
|
||||
$: runnables = getAppScripts($app.grid, $app.subgrids)
|
||||
|
||||
// When selected component changes, update selectedScriptComponentId
|
||||
$: if ($selectedComponent != selectedScriptComponentId) {
|
||||
@@ -53,7 +53,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mb-0 pb-0 pl-2 sticky top-0 bg-white border-b text-xs font-semibold">Runnables</div>
|
||||
<div
|
||||
class="sticky top-0 py-0.5 px-2 text-center bg-gray-200 text-gray-500 text-2xs font-bold tracking-wide border-b border-gray-300"
|
||||
>
|
||||
RUNNABLES
|
||||
</div>
|
||||
<div bind:this={list} class="grow flex flex-col gap-4">
|
||||
<PanelSection title="Inline scripts" smallPadding>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
|
||||
@@ -44,26 +44,28 @@ export function computeFields(schema: Schema, defaultUserInput: boolean, fields:
|
||||
|
||||
function processGridItemRunnable(gridItem: GridItem, list: AppScriptsList): AppScriptsList {
|
||||
const component: AppComponent = gridItem.data
|
||||
const componentInput = component.componentInput
|
||||
if (component.type === 'tablecomponent') {
|
||||
component.actionButtons.forEach((actionButton) => {
|
||||
if (actionButton.componentInput?.type !== 'runnable') {
|
||||
return
|
||||
}
|
||||
processRunnable(actionButton.componentInput.runnable, actionButton.id, list)
|
||||
})
|
||||
}
|
||||
if (componentInput?.type === 'runnable') {
|
||||
processRunnable(componentInput.runnable, gridItem.id, list)
|
||||
if (component) {
|
||||
const componentInput = component.componentInput
|
||||
if (component.type === 'tablecomponent') {
|
||||
component.actionButtons.forEach((actionButton) => {
|
||||
if (actionButton.componentInput?.type !== 'runnable') {
|
||||
return
|
||||
}
|
||||
processRunnable(actionButton.componentInput.runnable, actionButton.id, list)
|
||||
})
|
||||
}
|
||||
if (componentInput?.type === 'runnable') {
|
||||
processRunnable(componentInput.runnable, gridItem.id, list)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
export function getAppScripts(
|
||||
lazyGrid: GridItem[],
|
||||
grid: GridItem[],
|
||||
subgrids: Record<string, GridItem[]> | undefined
|
||||
): AppScriptsList {
|
||||
const scriptsList = lazyGrid.reduce(
|
||||
const scriptsList = grid.reduce(
|
||||
(acc, gridComponent) => processGridItemRunnable(gridComponent, acc),
|
||||
{ inline: [], imported: [] } as AppScriptsList
|
||||
)
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function addElementByType() {
|
||||
if (!Array.isArray(componentInput.value)) {
|
||||
componentInput.value = []
|
||||
}
|
||||
if (componentInput.subFieldType && componentInput.value) {
|
||||
if (componentInput.subFieldType === 'boolean') {
|
||||
componentInput.value.push(false)
|
||||
@@ -47,7 +50,7 @@
|
||||
</script>
|
||||
|
||||
<div class="flex gap-2 flex-col mt-2">
|
||||
{#if componentInput.value}
|
||||
{#if Array.isArray(componentInput.value)}
|
||||
{#each componentInput.value as value, index (index)}
|
||||
<div class="flex flex-row gap-2 items-center relative">
|
||||
<SubTypeEditor bind:componentInput bind:value />
|
||||
@@ -65,13 +68,8 @@
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
startIcon={{ icon: faPlus }}
|
||||
on:click={() => addElementByType()}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
{/if}
|
||||
<Button size="xs" color="light" startIcon={{ icon: faPlus }} on:click={() => addElementByType()}>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { faCopy, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faChevronDown, faChevronUp, faCopy, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, GridItem } from '../../types'
|
||||
import PanelSection from './common/PanelSection.svelte'
|
||||
@@ -24,15 +24,25 @@
|
||||
import { duplicateGridItem } from '../appUtils'
|
||||
import { deleteGridItem } from '../appUtils'
|
||||
import MoveToOtherGrid from './MoveToOtherGrid.svelte'
|
||||
import GridPane from './GridPane.svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
|
||||
export let component: AppComponent
|
||||
export let rowColumns = false
|
||||
export let onDelete: (() => void) | undefined = undefined
|
||||
export let parent: string | undefined
|
||||
export let noGrid = false
|
||||
export let duplicateMoveAllowed = true
|
||||
|
||||
const { app, staticOutputs, runnableComponents, selectedComponent, worldStore, focusedGrid } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
const {
|
||||
app,
|
||||
staticOutputs,
|
||||
runnableComponents,
|
||||
selectedComponent,
|
||||
worldStore,
|
||||
focusedGrid,
|
||||
stateId
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function duplicateElement(id: string) {
|
||||
$dirtyStore = true
|
||||
@@ -60,6 +70,8 @@
|
||||
onDelete?.()
|
||||
}
|
||||
|
||||
let viewCssOptions = false
|
||||
|
||||
$: extraLib =
|
||||
component?.componentInput?.type === 'template' && $worldStore
|
||||
? buildExtraLib($worldStore?.outputsById ?? {}, component?.id, false)
|
||||
@@ -106,38 +118,42 @@
|
||||
{:else if component.componentInput?.type === 'runnable' && component.componentInput !== undefined}
|
||||
<RunnableInputEditor
|
||||
bind:appInput={component.componentInput}
|
||||
defaultUserInput={component.type == 'formcomponent'}
|
||||
defaultUserInput={component.type == 'formcomponent' ||
|
||||
component.type == 'formbuttoncomponent'}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{#if component.componentInput?.type === 'runnable' && Object.keys(component.componentInput.fields ?? {}).length > 0}
|
||||
<div class="border w-full">
|
||||
<PanelSection
|
||||
smallPadding
|
||||
title={`Runnable Inputs (${
|
||||
Object.keys(component.componentInput.fields ?? {}).length
|
||||
})`}
|
||||
>
|
||||
<svelte:fragment slot="action">
|
||||
<Tooltip>
|
||||
The runnable inputs of a button component are not settable by the user. They must
|
||||
be defined statically or connected.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
{#key $stateId}
|
||||
{#if component.componentInput?.type === 'runnable'}
|
||||
{#if Object.keys(component.componentInput.fields ?? {}).length > 0}
|
||||
<div class="border w-full">
|
||||
<PanelSection
|
||||
smallPadding
|
||||
title={`Runnable Inputs (${
|
||||
Object.keys(component.componentInput.fields ?? {}).length
|
||||
})`}
|
||||
>
|
||||
<svelte:fragment slot="action">
|
||||
<Tooltip>
|
||||
The runnable inputs of a button component are not settable by the user. They
|
||||
must be defined statically or connected.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
|
||||
<InputsSpecsEditor
|
||||
id={component.id}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={component.componentInput.fields}
|
||||
userInputEnabled={component.type !== 'buttoncomponent'}
|
||||
{rowColumns}
|
||||
/>
|
||||
</PanelSection>
|
||||
</div>
|
||||
{/if}
|
||||
<InputsSpecsEditor
|
||||
id={component.id}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={component.componentInput.fields}
|
||||
userInputEnabled={component.type !== 'buttoncomponent'}
|
||||
{rowColumns}
|
||||
/>
|
||||
</PanelSection>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/key}
|
||||
</PanelSection>
|
||||
{/if}
|
||||
|
||||
{#if Object.values(component.configuration).length > 0}
|
||||
<PanelSection title={`Configuration (${Object.values(component.configuration).length})`}>
|
||||
<InputsSpecsEditor
|
||||
@@ -151,9 +167,9 @@
|
||||
|
||||
{#if component.type === 'tabscomponent'}
|
||||
<GridTab bind:tabs={component.tabs} {component} />
|
||||
{/if}
|
||||
|
||||
{#if component.type === 'tablecomponent' && Array.isArray(component.actionButtons)}
|
||||
{:else if component.type === 'verticalsplitpanescomponent' || component.type === 'horizontalsplitpanescomponent'}
|
||||
<GridPane bind:panes={component.panes} {component} />
|
||||
{:else if component.type === 'tablecomponent' && Array.isArray(component.actionButtons)}
|
||||
<TableActions id={component.id} bind:components={component.actionButtons} />
|
||||
{/if}
|
||||
|
||||
@@ -164,34 +180,50 @@
|
||||
|
||||
{#if Object.keys(component.customCss ?? {}).length > 0}
|
||||
<PanelSection title="Custom CSS">
|
||||
{#each Object.keys(component.customCss ?? {}) as name}
|
||||
{#if component?.customCss?.[name]}
|
||||
<div class="w-full mb-2">
|
||||
<CssProperty {name} bind:value={component.customCss[name]} />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
<div slot="action">
|
||||
<Button
|
||||
color="light"
|
||||
size="xs"
|
||||
endIcon={{ icon: viewCssOptions ? faChevronUp : faChevronDown }}
|
||||
on:click={() => (viewCssOptions = !viewCssOptions)}
|
||||
>
|
||||
{viewCssOptions ? 'Hide' : 'Show'}
|
||||
</Button>
|
||||
</div>
|
||||
{#if viewCssOptions}
|
||||
<div transition:slide>
|
||||
{#each Object.keys(component.customCss ?? {}) as name}
|
||||
{#if component?.customCss?.[name]}
|
||||
<div class="w-full mb-2">
|
||||
<CssProperty {name} bind:value={component.customCss[name]} />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</PanelSection>
|
||||
{/if}
|
||||
|
||||
<PanelSection title="Duplicate">
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
startIcon={{ icon: faCopy }}
|
||||
on:click={() => {
|
||||
if (component) {
|
||||
duplicateElement(component.id)
|
||||
}
|
||||
}}
|
||||
>
|
||||
Duplicate component: {component.id}
|
||||
</Button>
|
||||
</PanelSection>
|
||||
{#if duplicateMoveAllowed}
|
||||
<PanelSection title="Duplicate">
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
startIcon={{ icon: faCopy }}
|
||||
on:click={() => {
|
||||
if (component) {
|
||||
duplicateElement(component.id)
|
||||
}
|
||||
}}
|
||||
>
|
||||
Duplicate component: {component.id}
|
||||
</Button>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection title="Move to other grid">
|
||||
<MoveToOtherGrid bind:component {parent} />
|
||||
</PanelSection>
|
||||
<PanelSection title="Move to other grid">
|
||||
<MoveToOtherGrid bind:component {parent} />
|
||||
</PanelSection>
|
||||
{/if}
|
||||
|
||||
<PanelSection title="Danger zone">
|
||||
<Button
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { faPlus, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { deleteGridItem } from '../appUtils'
|
||||
import type { AppComponent } from '../component'
|
||||
import PanelSection from './common/PanelSection.svelte'
|
||||
|
||||
export let panes: number[]
|
||||
export let component: AppComponent
|
||||
|
||||
const { app, staticOutputs, runnableComponents } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function addTab() {
|
||||
const numberOfPanes = panes.length
|
||||
panes = Array(panes.length + 1)
|
||||
.fill(0)
|
||||
.map((_) => Math.floor(100 / (panes.length + 1)))
|
||||
|
||||
if (!$app.subgrids) {
|
||||
$app.subgrids = {}
|
||||
}
|
||||
|
||||
$app.subgrids[`${component.id}-${numberOfPanes}`] = []
|
||||
component.numberOfSubgrids = panes.length
|
||||
}
|
||||
|
||||
function deleteSubgrid(index: number) {
|
||||
let subgrid = `${component.id}-${index}`
|
||||
for (const item of $app!.subgrids![subgrid]) {
|
||||
const components = deleteGridItem($app, item.data, subgrid, false)
|
||||
console.log(components)
|
||||
for (const key in components) {
|
||||
delete $staticOutputs[key]
|
||||
delete $runnableComponents[key]
|
||||
}
|
||||
}
|
||||
$staticOutputs = $staticOutputs
|
||||
$runnableComponents = $runnableComponents
|
||||
for (let i = index; i < panes.length - 1; i++) {
|
||||
$app!.subgrids![`${component.id}-${i}`] = $app!.subgrids![`${component.id}-${i + 1}`]
|
||||
}
|
||||
panes.splice(index, 1)
|
||||
panes = panes
|
||||
component.numberOfSubgrids = panes.length
|
||||
$app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
<PanelSection title={`panes ${panes.length > 0 ? `(${panes.length})` : ''}`}>
|
||||
{#if panes.length == 0}
|
||||
<span class="text-xs text-gray-500">No panes</span>
|
||||
{/if}
|
||||
<div class="flex gap-2 flex-col mt-2">
|
||||
{#each panes as value, index (index)}
|
||||
<div class="flex flex-row gap-2 items-center relative">
|
||||
<input type="number" bind:value />
|
||||
|
||||
<div class="absolute top-1 right-1">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
deleteSubgrid(index)
|
||||
}}
|
||||
iconOnly
|
||||
btnClasses="!text-red-500"
|
||||
startIcon={{ icon: faTrashAlt }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
startIcon={{ icon: faPlus }}
|
||||
on:click={addTab}
|
||||
iconOnly
|
||||
/>
|
||||
</div>
|
||||
</PanelSection>
|
||||
@@ -44,6 +44,30 @@
|
||||
fieldType: 'boolean',
|
||||
type: 'eval',
|
||||
expr: 'false'
|
||||
},
|
||||
goto: {
|
||||
tooltip: 'Go to an url on success if not empty',
|
||||
fieldType: 'text',
|
||||
type: 'static',
|
||||
value: ''
|
||||
},
|
||||
gotoNewTab: {
|
||||
tooltip: 'Go to create a new tab',
|
||||
fieldType: 'boolean',
|
||||
type: 'static',
|
||||
value: true
|
||||
},
|
||||
beforeIcon: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'icon-select',
|
||||
onlyStatic: true
|
||||
},
|
||||
afterIcon: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'icon-select',
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
componentInput: {
|
||||
@@ -54,6 +78,9 @@
|
||||
value: undefined
|
||||
},
|
||||
recomputeIds: undefined,
|
||||
customCss: {
|
||||
button: { style: '', class: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { FileInput } from "../../../../common"
|
||||
import type { UploadAppInput } from "../../../inputType"
|
||||
import { FileInput } from '../../../../common'
|
||||
import type { UploadAppInput } from '../../../inputType'
|
||||
|
||||
export let componentInput: UploadAppInput | undefined
|
||||
</script>
|
||||
@@ -8,19 +8,17 @@
|
||||
<FileInput
|
||||
accept={componentInput?.fileUpload?.accept}
|
||||
multiple={componentInput?.fileUpload?.multiple}
|
||||
convertToBase64={componentInput?.fileUpload?.base64}
|
||||
convertTo={componentInput?.fileUpload?.convertTo}
|
||||
iconSize={24}
|
||||
class="text-sm py-4"
|
||||
on:change={({detail}) => {
|
||||
if(componentInput) {
|
||||
componentInput.value = componentInput?.fileUpload?.multiple
|
||||
? detail
|
||||
: detail?.[0]
|
||||
on:change={({ detail }) => {
|
||||
if (componentInput) {
|
||||
componentInput.value = componentInput?.fileUpload?.multiple ? detail : detail?.[0]
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="selected-title">
|
||||
<!-- Removing the title when there is a selected file -->
|
||||
<span></span>
|
||||
<span />
|
||||
</svelte:fragment>
|
||||
</FileInput>
|
||||
</FileInput>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ReadFileAs } from '../common/fileInput/model'
|
||||
import type { staticValues } from './editor/componentsPanel/componentStaticValues'
|
||||
import type { InlineScript } from './types'
|
||||
|
||||
@@ -110,10 +111,10 @@ type InputConfiguration<T extends InputType, U, V extends InputType> = {
|
||||
*/
|
||||
multiple?: boolean
|
||||
/**
|
||||
* Controls if the uploaded file(s) will be returned as `Base64` strings.
|
||||
* Controls if the uploaded file(s) will be converted or not.
|
||||
* @default false
|
||||
*/
|
||||
base64?: boolean
|
||||
convertTo?: ReadFileAs
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user