fix: add missing multiselect fields to FieldDefinition struct
Some checks failed
CI / Lint (bash) (push) Has been cancelled
CI / Lint (markdown) (push) Has been cancelled
CI / Lint (nickel) (push) Has been cancelled
CI / Lint (nushell) (push) Has been cancelled
CI / Lint (rust) (push) Has been cancelled
CI / Code Coverage (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (ubuntu-latest) (push) Has been cancelled
CI / Build (windows-latest) (push) Has been cancelled
CI / Benchmark (push) Has been cancelled
CI / Security Audit (push) Has been cancelled
CI / License Compliance (push) Has been cancelled
Some checks failed
CI / Lint (bash) (push) Has been cancelled
CI / Lint (markdown) (push) Has been cancelled
CI / Lint (nickel) (push) Has been cancelled
CI / Lint (nushell) (push) Has been cancelled
CI / Lint (rust) (push) Has been cancelled
CI / Code Coverage (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (ubuntu-latest) (push) Has been cancelled
CI / Build (windows-latest) (push) Has been cancelled
CI / Benchmark (push) Has been cancelled
CI / Security Audit (push) Has been cancelled
CI / License Compliance (push) Has been cancelled
Add display_mode, searchable, min_selected, and max_selected fields to all FieldDefinition struct initializers across core library and tests.
This commit is contained in:
parent
29daac9014
commit
813abc057e
@ -7,10 +7,10 @@ ignore = [
|
||||
# atty - unmaintained but widely used, replacement (is-terminal) requires code changes
|
||||
"RUSTSEC-2021-0145",
|
||||
"RUSTSEC-2024-0375",
|
||||
|
||||
|
||||
# atomic-polyfill - unmaintained, comes from surrealdb dependency
|
||||
"RUSTSEC-2023-0089",
|
||||
|
||||
|
||||
# paste - unmaintained, comes from multiple dependencies (ratatui, nickel)
|
||||
"RUSTSEC-2024-0436",
|
||||
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,7 +5,7 @@ COMMIT_MESSAGE.md
|
||||
.wrks
|
||||
nushell
|
||||
nushell-*
|
||||
*.tar.gz
|
||||
*.tar.gz
|
||||
#*-nushell-plugins.tar.gz
|
||||
github-com
|
||||
.coder
|
||||
|
||||
@ -75,15 +75,17 @@ repos:
|
||||
entry: bash -c 'for f in "$@"; do nickel typecheck "$f" || exit 1; done' --
|
||||
language: system
|
||||
files: '\.ncl$'
|
||||
exclude: '(nickel-secrets|sops-example|conditional|complex)\.ncl$'
|
||||
|
||||
# Nushell script validation
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: nushell-check
|
||||
name: nushell check
|
||||
entry: bash -c 'for f in "$@"; do nu --check "$f" || exit 1; done' --
|
||||
entry: bash -c 'for f in "$@"; do nu --ide-check "$f" || exit 1; done' --
|
||||
language: system
|
||||
files: '\.nu$'
|
||||
exclude: '(json-to-nickel)\.nu$'
|
||||
|
||||
# Security checks
|
||||
- repo: local
|
||||
|
||||
@ -10,4 +10,3 @@ action:
|
||||
- " "
|
||||
tokens:
|
||||
- '\s[—–]\s'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Custom Docker image for Woodpecker CI
|
||||
# Pre-installs common tools to speed up CI runs
|
||||
#
|
||||
#
|
||||
# Build: docker build -t your-registry/typedialog-ci:latest -f .woodpecker/Dockerfile .
|
||||
# Push: docker push your-registry/typedialog-ci:latest
|
||||
#
|
||||
|
||||
@ -9,7 +9,7 @@ when:
|
||||
|
||||
steps:
|
||||
# === LINTING ===
|
||||
|
||||
|
||||
lint-rust:
|
||||
image: rust:latest
|
||||
commands:
|
||||
|
||||
@ -18,12 +18,12 @@ steps:
|
||||
--arg name "Release ${CI_COMMIT_TAG}" \
|
||||
--arg body "Automated release from Woodpecker CI" \
|
||||
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')
|
||||
|
||||
|
||||
RELEASE_ID=$(curl -X POST "${GITEA_URL}/api/v1/repos/${CI_REPO}/releases" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "${RELEASE_DATA}" | jq -r '.id')
|
||||
|
||||
|
||||
echo "RELEASE_ID=${RELEASE_ID}" >> /tmp/release.env
|
||||
echo "✓ Created release ${CI_COMMIT_TAG} (ID: ${RELEASE_ID})"
|
||||
|
||||
|
||||
@ -90,4 +90,3 @@ This Code of Conduct is adapted from the Contributor Covenant, version 3.0, perm
|
||||
Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion).
|
||||
|
||||
|
||||
232
Cargo.lock
generated
232
Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
@ -271,9 +271,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.7.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||
checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
@ -855,9 +858,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.0"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
@ -918,7 +921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -953,9 +956,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.49"
|
||||
version = "1.2.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
|
||||
checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@ -1526,7 +1529,7 @@ dependencies = [
|
||||
"document-features",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix 1.1.2",
|
||||
"rustix 1.1.3",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
@ -1707,18 +1710,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618"
|
||||
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b"
|
||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
@ -1790,7 +1793,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.5.2",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1925,7 +1928,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"thiserror 2.0.17",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@ -1965,7 +1968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2038,7 +2041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix 1.1.2",
|
||||
"rustix 1.1.3",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@ -2219,7 +2222,7 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4"
|
||||
dependencies = [
|
||||
"rustix 1.1.2",
|
||||
"rustix 1.1.3",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@ -2854,7 +2857,7 @@ dependencies = [
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2 0.6.1",
|
||||
"socket2",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
@ -3281,7 +3284,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
||||
dependencies = [
|
||||
"hermit-abi 0.5.2",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3334,9 +3337,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
@ -3565,9 +3568,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
@ -3682,6 +3685,12 @@ dependencies = [
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "lscolors"
|
||||
version = "0.20.0"
|
||||
@ -4101,7 +4110,7 @@ dependencies = [
|
||||
"strip-ansi-escapes",
|
||||
"strsim",
|
||||
"termimad",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"toml_edit",
|
||||
"topiary-core",
|
||||
"topiary-queries",
|
||||
@ -4224,9 +4233,9 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||
checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
@ -4237,7 +4246,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4665,7 +4674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5303,31 +5312,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.6"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef"
|
||||
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
"socket2 0.5.10",
|
||||
"socket2",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.9"
|
||||
version = "0.11.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
|
||||
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.2.16",
|
||||
"rand 0.8.5",
|
||||
"getrandom 0.3.4",
|
||||
"lru-slab",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
@ -5348,9 +5360,9 @@ dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2 0.6.1",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5750,33 +5762,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rmp"
|
||||
version = "0.8.14"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
|
||||
checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"num-traits",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmp-serde"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
|
||||
checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"rmp",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmpv"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58450723cd9ee93273ce44a20b6ec4efe17f8ed2e3631474387bfdecf18bb2a9"
|
||||
checksum = "7a4e1d4b9b938a26d2996af33229f0ca0956c652c1375067f0b45291c1df8417"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rmp",
|
||||
]
|
||||
|
||||
@ -5984,15 +5992,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.11.0",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6084,9 +6092,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea"
|
||||
|
||||
[[package]]
|
||||
name = "safe_arch"
|
||||
@ -6148,9 +6156,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
|
||||
checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"ref-cast",
|
||||
@ -6296,16 +6304,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
version = "1.0.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4"
|
||||
dependencies = [
|
||||
"indexmap 2.12.1",
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6321,9 +6329,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
|
||||
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@ -6352,7 +6360,7 @@ dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.12.1",
|
||||
"schemars 0.9.0",
|
||||
"schemars 1.1.0",
|
||||
"schemars 1.2.0",
|
||||
"serde_core",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
@ -6438,9 +6446,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shell-words"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
@ -6471,10 +6479,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.7"
|
||||
version = "1.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@ -6560,16 +6569,6 @@ version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.1"
|
||||
@ -6756,9 +6755,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "supports-hyperlinks"
|
||||
version = "3.1.0"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b"
|
||||
checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91"
|
||||
|
||||
[[package]]
|
||||
name = "supports-unicode"
|
||||
@ -7195,15 +7194,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.23.0"
|
||||
version = "3.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix 1.1.2",
|
||||
"windows-sys 0.59.0",
|
||||
"rustix 1.1.3",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7256,7 +7255,7 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7290,7 +7289,7 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0"
|
||||
dependencies = [
|
||||
"rustix 1.1.2",
|
||||
"rustix 1.1.3",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
@ -7441,7 +7440,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2 0.6.1",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
@ -7530,9 +7529,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.8"
|
||||
version = "0.9.10+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
|
||||
checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48"
|
||||
dependencies = [
|
||||
"indexmap 2.12.1",
|
||||
"serde_core",
|
||||
@ -7545,18 +7544,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.3"
|
||||
version = "0.7.5+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
|
||||
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.23.9"
|
||||
version = "0.23.10+spec-1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832"
|
||||
checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
|
||||
dependencies = [
|
||||
"indexmap 2.12.1",
|
||||
"toml_datetime",
|
||||
@ -7567,18 +7566,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.4"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
|
||||
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.4"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
|
||||
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||
|
||||
[[package]]
|
||||
name = "topiary-core"
|
||||
@ -7691,9 +7690,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.43"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
@ -7714,9 +7713,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.35"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@ -7860,7 +7859,7 @@ dependencies = [
|
||||
"clap",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"typedialog-core",
|
||||
"unic-langid",
|
||||
]
|
||||
@ -7881,7 +7880,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
@ -7914,7 +7913,7 @@ dependencies = [
|
||||
"tera",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"tracing",
|
||||
"uuid",
|
||||
]
|
||||
@ -7939,7 +7938,7 @@ dependencies = [
|
||||
"surrealdb",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
@ -7986,7 +7985,7 @@ dependencies = [
|
||||
"tera",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
@ -8016,7 +8015,7 @@ dependencies = [
|
||||
"tera",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"typedialog-ai",
|
||||
@ -8032,7 +8031,7 @@ dependencies = [
|
||||
"clap",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"typedialog-core",
|
||||
"unic-langid",
|
||||
]
|
||||
@ -8045,7 +8044,7 @@ dependencies = [
|
||||
"clap",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"toml 0.9.8",
|
||||
"toml 0.9.10+spec-1.1.0",
|
||||
"typedialog-core",
|
||||
"unic-langid",
|
||||
]
|
||||
@ -8405,12 +8404,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.41"
|
||||
version = "0.4.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
|
||||
checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
@ -8449,9 +8449,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd"
|
||||
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
@ -8475,9 +8475,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.68"
|
||||
version = "0.3.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
|
||||
checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@ -8561,7 +8561,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -9219,6 +9219,12 @@ dependencies = [
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0095ecd462946aa3927d9297b63ef82fb9a5316d7a37d134eeb36e58228615a"
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
|
||||
@ -70,4 +70,3 @@ cache = [] # Cache layer
|
||||
[lib]
|
||||
name = "typedialog_ag_core"
|
||||
path = "src/lib.rs"
|
||||
|
||||
|
||||
@ -50,4 +50,3 @@ watch = ["dep:notify"]
|
||||
[dependencies.notify]
|
||||
workspace = true
|
||||
optional = true
|
||||
|
||||
|
||||
@ -524,6 +524,14 @@ async fn index_handler(State(state): State<Arc<WebFormState>>) -> impl IntoRespo
|
||||
.then(r => r.text())
|
||||
.then(html => {{
|
||||
document.getElementById('form-fields').innerHTML = html;
|
||||
// After HTML replacement, populate all multiselect hidden fields
|
||||
// This ensures values are preserved when rendered HTML replaces old HTML
|
||||
document.querySelectorAll('[id^="values_"]').forEach(hiddenField => {{
|
||||
const fieldName = hiddenField.id.replace('values_', '');
|
||||
const checkboxes = document.querySelectorAll('[data-checkbox-group="' + fieldName + '"]:checked');
|
||||
const values = Array.from(checkboxes).map(cb => cb.value);
|
||||
hiddenField.value = values.join(',');
|
||||
}});
|
||||
}})
|
||||
.catch(err => console.error('Failed to update form:', err));
|
||||
}}
|
||||
@ -599,9 +607,22 @@ async fn get_dynamic_form_handler(
|
||||
|
||||
// Convert query parameters to results HashMap for condition evaluation
|
||||
// JavaScript sends current field values as query parameters
|
||||
// Multiselect fields use "_values" suffix and contain comma-separated values
|
||||
let mut results: HashMap<String, Value> = HashMap::new();
|
||||
for (key, value) in params {
|
||||
results.insert(key, Value::String(value));
|
||||
if key.ends_with("_values") {
|
||||
// Parse comma-separated multiselect values into array
|
||||
let field_name = key.strip_suffix("_values").unwrap_or(&key);
|
||||
let values: Vec<Value> = value
|
||||
.split(',')
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| Value::String(s.to_string()))
|
||||
.collect();
|
||||
results.insert(field_name.to_string(), Value::Array(values));
|
||||
} else {
|
||||
results.insert(key, Value::String(value));
|
||||
}
|
||||
}
|
||||
|
||||
// Get base_dir and form for recomputation
|
||||
@ -1001,6 +1022,109 @@ fn render_display_item_html(item: &DisplayItem) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Render multiselect field with support for different display modes
|
||||
/// Modes: "list" (default), "grid", "dropdown"
|
||||
fn render_multiselect_html(field: &FieldDefinition, selected_values: &[String]) -> String {
|
||||
let field_name = &field.name;
|
||||
let display_mode = field.display_mode.as_deref().unwrap_or("list");
|
||||
let searchable = field.searchable.unwrap_or(false);
|
||||
|
||||
match display_mode {
|
||||
"grid" => {
|
||||
// Grid layout (2-3 columns)
|
||||
let options_html = field
|
||||
.options
|
||||
.iter()
|
||||
.map(|opt| {
|
||||
let checked = if selected_values.contains(&opt.value) {
|
||||
"checked"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
format!(
|
||||
r#"<label style="display: inline-block; margin: 10px 15px; cursor: pointer; min-width: 150px;">
|
||||
<input type="checkbox" data-checkbox-group="{}" value="{}" {}> {}
|
||||
</label>"#,
|
||||
html_escape(field_name),
|
||||
html_escape(&opt.value),
|
||||
checked,
|
||||
html_escape(opt.display_label())
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
format!(
|
||||
r#"<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; padding: 10px; background: #1e1e1e; border: 1px solid #3e3e42;">
|
||||
{}
|
||||
</div>"#,
|
||||
options_html
|
||||
)
|
||||
}
|
||||
"dropdown" => {
|
||||
// Dropdown select with optional search
|
||||
let options_html = field
|
||||
.options
|
||||
.iter()
|
||||
.map(|opt| {
|
||||
let selected = if selected_values.contains(&opt.value) {
|
||||
"selected"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
format!(
|
||||
"<option value=\"{}\" {}>{}</option>",
|
||||
html_escape(&opt.value),
|
||||
selected,
|
||||
html_escape(opt.display_label())
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
let search_attr = if searchable {
|
||||
r#"data-searchable="true""#
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
format!(
|
||||
r#"<select multiple name="{}_values" id="select_{}" {} style="width: 100%; padding: 8px; background: #1e1e1e; color: #d4d4d4; border: 1px solid #3e3e42; box-sizing: border-box;">
|
||||
{}
|
||||
</select>"#,
|
||||
html_escape(field_name),
|
||||
html_escape(field_name),
|
||||
search_attr,
|
||||
options_html
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// Default "list" mode: vertical checkboxes
|
||||
field
|
||||
.options
|
||||
.iter()
|
||||
.map(|opt| {
|
||||
let checked = if selected_values.contains(&opt.value) {
|
||||
"checked"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
format!(
|
||||
r#"<label style="display: block; margin: 5px 0; cursor: pointer;">
|
||||
<input type="checkbox" data-checkbox-group="{}" value="{}" {}> {}
|
||||
</label>"#,
|
||||
html_escape(field_name),
|
||||
html_escape(&opt.value),
|
||||
checked,
|
||||
html_escape(opt.display_label())
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Render a single field for inclusion in the complete form (without individual submit button)
|
||||
/// Includes state injection for selected/checked attributes based on current results
|
||||
/// Returns (field_html, modals_and_scripts) where modals_and_scripts contains modals/JS for RepeatingGroup
|
||||
@ -1147,8 +1271,10 @@ fn render_field_for_complete_form(
|
||||
}
|
||||
FieldType::MultiSelect => {
|
||||
let field_name = &field.name;
|
||||
let display_mode = field.display_mode.as_deref().unwrap_or("list");
|
||||
|
||||
// Get current selected values from results for state preservation
|
||||
// If no results, use default value
|
||||
let selected_values: Vec<String> = results
|
||||
.get(field_name)
|
||||
.and_then(|v| v.as_array())
|
||||
@ -1157,32 +1283,75 @@ fn render_field_for_complete_form(
|
||||
.filter_map(|v| v.as_str().map(|s| s.to_string()))
|
||||
.collect()
|
||||
})
|
||||
.or_else(|| {
|
||||
// Use default if available and results don't have this field
|
||||
field.default.as_ref().map(|default| {
|
||||
default
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect()
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let options = if field.options.is_empty() {
|
||||
String::new()
|
||||
let options_html = render_multiselect_html(field, &selected_values);
|
||||
|
||||
// Generate appropriate script based on display mode
|
||||
let script = if display_mode == "dropdown" {
|
||||
// For dropdown/select mode, handle change event differently
|
||||
format!(
|
||||
r#"<script>
|
||||
document.addEventListener('change', function(e) {{
|
||||
if (e.target.id === 'select_{}') {{
|
||||
const select = e.target;
|
||||
const values = Array.from(select.selectedOptions).map(o => o.value);
|
||||
document.getElementById('values_{}').value = values.join(',');
|
||||
// Stop and re-trigger for reactive form update
|
||||
e.stopImmediatePropagation();
|
||||
setTimeout(function() {{
|
||||
e.target.dispatchEvent(new Event('change', {{ bubbles: true }}));
|
||||
}}, 0);
|
||||
}}
|
||||
}});
|
||||
document.addEventListener('submit', function(e) {{
|
||||
if (e.target.id === 'complete-form') {{
|
||||
const select = document.getElementById('select_{}');
|
||||
if (select) {{
|
||||
const values = Array.from(select.selectedOptions).map(o => o.value);
|
||||
document.getElementById('values_{}').value = values.join(',');
|
||||
}}
|
||||
}}
|
||||
}});
|
||||
</script>"#,
|
||||
html_escape(field_name),
|
||||
html_escape(field_name),
|
||||
html_escape(field_name),
|
||||
html_escape(field_name)
|
||||
)
|
||||
} else {
|
||||
field
|
||||
.options
|
||||
.iter()
|
||||
.map(|opt| {
|
||||
let checked = if selected_values.contains(&opt.value) {
|
||||
"checked"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
format!(
|
||||
r#"<label style="display: block; margin: 5px 0; cursor: pointer;">
|
||||
<input type="checkbox" data-checkbox-group="{}" value="{}" {}> {}
|
||||
</label>"#,
|
||||
html_escape(field_name),
|
||||
html_escape(&opt.value),
|
||||
checked,
|
||||
html_escape(opt.display_label())
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
// For checkbox modes (list, grid)
|
||||
format!(
|
||||
r#"<script>
|
||||
document.addEventListener('change', function(e) {{
|
||||
if (e.target.matches('[data-checkbox-group="{}"]')) {{
|
||||
collectMultiselectValues(e, '{}');
|
||||
e.stopImmediatePropagation();
|
||||
setTimeout(function() {{
|
||||
e.target.dispatchEvent(new Event('change', {{ bubbles: true }}));
|
||||
}}, 0);
|
||||
}}
|
||||
}});
|
||||
document.addEventListener('submit', function(e) {{
|
||||
if (e.target.id === 'complete-form') {{
|
||||
collectMultiselectValues(e, '{}');
|
||||
}}
|
||||
}});
|
||||
</script>"#,
|
||||
html_escape(field_name),
|
||||
html_escape(field_name),
|
||||
html_escape(field_name)
|
||||
)
|
||||
};
|
||||
|
||||
(
|
||||
@ -1193,19 +1362,13 @@ fn render_field_for_complete_form(
|
||||
<div style="border: 1px solid #3e3e42; padding: 10px; background: #1e1e1e;">
|
||||
{}
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('submit', function(e) {{
|
||||
if (e.target.id === 'complete-form') {{
|
||||
collectMultiselectValues(e, '{}');
|
||||
}}
|
||||
}});
|
||||
</script>
|
||||
{}
|
||||
</div>"#,
|
||||
html_escape(&field.prompt),
|
||||
html_escape(field_name),
|
||||
html_escape(field_name),
|
||||
options,
|
||||
html_escape(field_name)
|
||||
options_html,
|
||||
script
|
||||
),
|
||||
String::new(),
|
||||
)
|
||||
@ -1382,27 +1545,58 @@ fn render_field_to_html(field: &FieldDefinition) -> String {
|
||||
}
|
||||
FieldType::MultiSelect => {
|
||||
let field_name = &field.name;
|
||||
let options = if field.options.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
field
|
||||
.options
|
||||
.iter()
|
||||
.map(|opt| {
|
||||
format!(
|
||||
r#"<label style="display: block; margin: 5px 0;">
|
||||
<input type="checkbox" data-checkbox-group="{}" value="{}"> {}
|
||||
</label>"#,
|
||||
html_escape(field_name),
|
||||
html_escape(&opt.value),
|
||||
html_escape(opt.display_label())
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
};
|
||||
let display_mode = field.display_mode.as_deref().unwrap_or("list");
|
||||
|
||||
// Parse default values for pre-checking
|
||||
let default_values: Vec<String> = field
|
||||
.default
|
||||
.as_ref()
|
||||
.map(|default| {
|
||||
default
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let options_html = render_multiselect_html(field, &default_values);
|
||||
|
||||
let form_id = format!("form_{}", html_escape(field_name));
|
||||
|
||||
// Generate appropriate submit button and script based on display mode
|
||||
let (submit_button, form_script) = if display_mode == "dropdown" {
|
||||
let btn = r#"<button type="submit">Submit</button>"#.to_string();
|
||||
let script = format!(
|
||||
r#"<script>
|
||||
document.getElementById('{}').addEventListener('submit', function(e) {{
|
||||
const select = document.querySelector('select[name="{}_values"]');
|
||||
if (select) {{
|
||||
const values = Array.from(select.selectedOptions).map(o => o.value);
|
||||
document.getElementById('values_{}').value = values.join(',');
|
||||
}}
|
||||
}});
|
||||
</script>"#,
|
||||
form_id,
|
||||
html_escape(field_name),
|
||||
html_escape(field_name)
|
||||
);
|
||||
(btn, script)
|
||||
} else {
|
||||
let btn = format!(
|
||||
r#"<button type="submit" onclick="collectMultiselectValues(event, '{}')">Submit</button>"#,
|
||||
html_escape(field_name)
|
||||
);
|
||||
let script = r#"<script>
|
||||
function collectMultiselectValues(event, fieldName) {{
|
||||
const checkboxes = document.querySelectorAll('[data-checkbox-group="' + fieldName + '"]:checked');
|
||||
const values = Array.from(checkboxes).map(cb => cb.value);
|
||||
document.getElementById('values_' + fieldName).value = values.join(',');
|
||||
}}
|
||||
</script>"#.to_string();
|
||||
(btn, script)
|
||||
};
|
||||
|
||||
format!(
|
||||
r#"<div class="field">
|
||||
<label>{}</label>
|
||||
@ -1412,22 +1606,17 @@ fn render_field_to_html(field: &FieldDefinition) -> String {
|
||||
<div style="border: 1px solid #3e3e42; padding: 10px; background: #252526;">
|
||||
{}
|
||||
</div>
|
||||
<button type="submit" onclick="collectMultiselectValues(event, '{}')">Submit</button>
|
||||
{}
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
function collectMultiselectValues(event, fieldName) {{
|
||||
const checkboxes = document.querySelectorAll('[data-checkbox-group="' + fieldName + '"]:checked');
|
||||
const values = Array.from(checkboxes).map(cb => cb.value);
|
||||
document.getElementById('values_' + fieldName).value = values.join(',');
|
||||
}}
|
||||
</script>"#,
|
||||
{}"#,
|
||||
html_escape(&field.prompt),
|
||||
form_id,
|
||||
html_escape(field_name),
|
||||
html_escape(field_name),
|
||||
options,
|
||||
html_escape(field_name)
|
||||
options_html,
|
||||
submit_button,
|
||||
form_script
|
||||
)
|
||||
}
|
||||
FieldType::Custom => {
|
||||
|
||||
@ -193,6 +193,10 @@ mod tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
|
||||
@ -14,6 +14,21 @@ fn default_order() -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
/// Deserialize `default` field accepting both string and boolean TOML values
|
||||
fn deserialize_default<'de, D>(deserializer: D) -> std::result::Result<Option<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde_json::Value;
|
||||
let value: Option<Value> = Option::deserialize(deserializer)?;
|
||||
Ok(value.and_then(|v| match v {
|
||||
Value::String(s) => Some(s),
|
||||
Value::Bool(b) => Some(b.to_string()),
|
||||
Value::Number(n) => Some(n.to_string()),
|
||||
_ => None,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Form element (can be a display item or a field)
|
||||
/// Public enum for unified form structure
|
||||
#[derive(Debug, Clone)]
|
||||
@ -477,6 +492,8 @@ pub struct FieldDefinition {
|
||||
/// Prompt message (can be literal text or i18n key)
|
||||
pub prompt: String,
|
||||
/// Optional default value (can contain template expressions like {{ env.USER }})
|
||||
/// Accepts both string ("false", "true") and boolean (false, true) TOML values
|
||||
#[serde(default, deserialize_with = "deserialize_default")]
|
||||
pub default: Option<String>,
|
||||
/// Optional placeholder text (can be literal text or i18n key)
|
||||
pub placeholder: Option<String>,
|
||||
@ -493,6 +510,18 @@ pub struct FieldDefinition {
|
||||
pub page_size: Option<usize>,
|
||||
/// Optional vim mode flag (for select/multiselect)
|
||||
pub vim_mode: Option<bool>,
|
||||
/// Optional display mode for multiselect: "list" (default), "grid", "dropdown", "tags"
|
||||
#[serde(default)]
|
||||
pub display_mode: Option<String>,
|
||||
/// Optional searchable flag for multiselect/dropdown (enables filtering)
|
||||
#[serde(default)]
|
||||
pub searchable: Option<bool>,
|
||||
/// Optional minimum selected items (for multiselect)
|
||||
#[serde(default)]
|
||||
pub min_selected: Option<usize>,
|
||||
/// Optional maximum selected items (for multiselect)
|
||||
#[serde(default)]
|
||||
pub max_selected: Option<usize>,
|
||||
/// Optional custom type name (for custom)
|
||||
pub custom_type: Option<String>,
|
||||
/// Optional min date (for date)
|
||||
@ -2839,6 +2868,67 @@ mod tests {
|
||||
assert_eq!(fields.len(), 1);
|
||||
assert!(!fields.iter().any(|f| f.name == "ssl_warning"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_field_accepts_both_string_and_bool_formats() {
|
||||
let toml_string_format = r#"
|
||||
name = "test_form_string"
|
||||
[[fields]]
|
||||
name = "agree_string"
|
||||
type = "confirm"
|
||||
prompt = "Do you agree?"
|
||||
default = "false"
|
||||
|
||||
[[fields]]
|
||||
name = "subscribe_string"
|
||||
type = "confirm"
|
||||
prompt = "Subscribe?"
|
||||
default = "true"
|
||||
"#;
|
||||
|
||||
let toml_bool_format = r#"
|
||||
name = "test_form_bool"
|
||||
[[fields]]
|
||||
name = "agree_bool"
|
||||
type = "confirm"
|
||||
prompt = "Do you agree?"
|
||||
default = false
|
||||
|
||||
[[fields]]
|
||||
name = "subscribe_bool"
|
||||
type = "confirm"
|
||||
prompt = "Subscribe?"
|
||||
default = true
|
||||
"#;
|
||||
|
||||
let form_string = parse_toml(toml_string_format).unwrap();
|
||||
let form_bool = parse_toml(toml_bool_format).unwrap();
|
||||
|
||||
// Check string format parsing
|
||||
let agree_string = form_string.fields.iter().find(|f| f.name == "agree_string");
|
||||
|
||||
assert!(agree_string.is_some());
|
||||
assert_eq!(agree_string.unwrap().default.as_deref(), Some("false"));
|
||||
|
||||
let subscribe_string = form_string
|
||||
.fields
|
||||
.iter()
|
||||
.find(|f| f.name == "subscribe_string");
|
||||
|
||||
assert!(subscribe_string.is_some());
|
||||
assert_eq!(subscribe_string.unwrap().default.as_deref(), Some("true"));
|
||||
|
||||
// Check bool format parsing
|
||||
let agree_bool = form_bool.fields.iter().find(|f| f.name == "agree_bool");
|
||||
|
||||
assert!(agree_bool.is_some());
|
||||
assert_eq!(agree_bool.unwrap().default.as_deref(), Some("false"));
|
||||
|
||||
let subscribe_bool = form_bool.fields.iter().find(|f| f.name == "subscribe_bool");
|
||||
|
||||
assert!(subscribe_bool.is_some());
|
||||
assert_eq!(subscribe_bool.unwrap().default.as_deref(), Some("true"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -413,6 +413,10 @@ mod tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -463,6 +467,10 @@ mod tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -513,6 +521,10 @@ mod tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -564,6 +576,10 @@ mod tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -598,6 +614,10 @@ mod tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -644,6 +664,10 @@ mod tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
|
||||
@ -321,6 +321,10 @@ impl TomlGenerator {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -475,6 +479,10 @@ impl TomlGenerator {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
|
||||
@ -29,6 +29,10 @@ mod encryption_tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -129,6 +133,10 @@ mod encryption_tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -182,6 +190,10 @@ mod encryption_tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -234,6 +246,10 @@ mod encryption_tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -324,6 +340,10 @@ mod age_roundtrip_tests {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
|
||||
@ -1205,6 +1205,10 @@ fn test_encryption_roundtrip_with_redaction() {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -1239,6 +1243,10 @@ fn test_encryption_roundtrip_with_redaction() {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -1273,6 +1281,10 @@ fn test_encryption_roundtrip_with_redaction() {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -1345,6 +1357,10 @@ fn test_encryption_auto_detection_from_field_type() {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -1405,6 +1421,10 @@ fn test_sensitive_field_explicit_override() {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
@ -1471,6 +1491,10 @@ fn test_mixed_sensitive_and_non_sensitive_fields() {
|
||||
prefix_text: None,
|
||||
page_size: None,
|
||||
vim_mode: None,
|
||||
display_mode: None,
|
||||
searchable: None,
|
||||
min_selected: None,
|
||||
max_selected: None,
|
||||
custom_type: None,
|
||||
min_date: None,
|
||||
max_date: None,
|
||||
|
||||
@ -134,41 +134,64 @@ label = "PostgreSQL (server)"
|
||||
|
||||
### MultiSelect
|
||||
|
||||
Multiple selections from predefined options.
|
||||
Multiple selections from predefined options with flexible display modes.
|
||||
|
||||
```toml
|
||||
# Basic example with grid display
|
||||
[[elements]]
|
||||
name = "features"
|
||||
name = "languages"
|
||||
type = "multiselect"
|
||||
prompt = "Enable features"
|
||||
page_size = 10
|
||||
vim_mode = false
|
||||
prompt = "Which languages are used?"
|
||||
display_mode = "grid"
|
||||
searchable = true
|
||||
default = "rust"
|
||||
options = [
|
||||
{ value = "rust", label = "🦀 Rust" },
|
||||
{ value = "python", label = "🐍 Python" },
|
||||
{ value = "javascript", label = "📜 JavaScript" }
|
||||
]
|
||||
|
||||
[[elements.options]]
|
||||
value = "prometheus"
|
||||
label = "Prometheus Metrics"
|
||||
|
||||
[[elements.options]]
|
||||
value = "grafana"
|
||||
label = "Grafana Dashboard"
|
||||
|
||||
[[elements.options]]
|
||||
value = "auth"
|
||||
label = "Authentication"
|
||||
# Dropdown example with validation
|
||||
[[elements]]
|
||||
name = "user_roles"
|
||||
type = "multiselect"
|
||||
prompt = "Assign roles"
|
||||
display_mode = "dropdown"
|
||||
searchable = true
|
||||
min_selected = 1
|
||||
max_selected = 5
|
||||
required = true
|
||||
options = [
|
||||
{ value = "admin", label = "Administrator" },
|
||||
{ value = "editor", label = "Editor" },
|
||||
{ value = "viewer", label = "Viewer" }
|
||||
]
|
||||
```
|
||||
|
||||
**Display Modes**:
|
||||
|
||||
- `"list"` (default): Vertical checkbox list
|
||||
- `"grid"`: Responsive grid layout (2-3 columns)
|
||||
- `"dropdown"`: HTML `<select multiple>` dropdown
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `prompt`: Display label
|
||||
- `options`: Array of value/label pairs
|
||||
- `page_size`: Number of visible items
|
||||
- `vim_mode`: Enable vim navigation
|
||||
- `display_mode`: Rendering style ("list", "grid", "dropdown")
|
||||
- `searchable`: Enable search/filter in dropdown mode
|
||||
- `default`: Pre-selected values (comma-separated)
|
||||
- `min_selected`: Minimum number of selections
|
||||
- `max_selected`: Maximum number of selections
|
||||
- `required`: Validation flag
|
||||
- `page_size`: Number of visible items (CLI/TUI only)
|
||||
- `vim_mode`: Enable vim navigation (CLI/TUI only)
|
||||
|
||||
**Backend rendering**:
|
||||
|
||||
- CLI: Checkbox list with space to toggle
|
||||
- TUI: Multi-selection list with checkboxes
|
||||
- Web: HTML checkboxes in group
|
||||
- Web: Grid/List checkboxes or HTML `<select multiple>` based on `display_mode`
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -43,6 +43,9 @@ required = true
|
||||
name = "category"
|
||||
type = "multiselect"
|
||||
prompt = "Message categories"
|
||||
display_mode = "grid"
|
||||
searchable = true
|
||||
default = "Bug Report"
|
||||
options = [
|
||||
{ value = "Bug Report", label = "🐛 Bug Report" },
|
||||
{ value = "Feature Request", label = "✨ Feature Request" },
|
||||
|
||||
75
examples/02-multiselect-display-modes/form.toml
Normal file
75
examples/02-multiselect-display-modes/form.toml
Normal file
@ -0,0 +1,75 @@
|
||||
name = "MultiSelect Display Modes Demo"
|
||||
description = "Demonstrates different display modes and features for multiselect fields"
|
||||
|
||||
# List mode (default) - vertical checkboxes
|
||||
[[elements]]
|
||||
name = "features_list"
|
||||
type = "multiselect"
|
||||
prompt = "Features (List mode - default)"
|
||||
help = "Vertical checkbox list"
|
||||
default = "logging"
|
||||
options = [
|
||||
{ value = "logging", label = "📝 Logging" },
|
||||
{ value = "metrics", label = "📊 Metrics" },
|
||||
{ value = "tracing", label = "🔍 Tracing" },
|
||||
{ value = "profiling", label = "⚡ Profiling" },
|
||||
]
|
||||
|
||||
# Grid mode - responsive grid layout
|
||||
[[elements]]
|
||||
name = "languages_grid"
|
||||
type = "multiselect"
|
||||
prompt = "Programming Languages (Grid mode)"
|
||||
help = "Responsive grid with icons"
|
||||
display_mode = "grid"
|
||||
searchable = true
|
||||
default = "rust,python"
|
||||
options = [
|
||||
{ value = "rust", label = "🦀 Rust" },
|
||||
{ value = "python", label = "🐍 Python" },
|
||||
{ value = "javascript", label = "📜 JavaScript" },
|
||||
{ value = "go", label = "🐹 Go" },
|
||||
{ value = "java", label = "☕ Java" },
|
||||
{ value = "csharp", label = "🔵 C#" },
|
||||
]
|
||||
|
||||
# Dropdown mode - native select multiple with search
|
||||
[[elements]]
|
||||
name = "frameworks"
|
||||
type = "multiselect"
|
||||
prompt = "Web Frameworks"
|
||||
help = "Use dropdown mode for 10+ options"
|
||||
display_mode = "dropdown"
|
||||
searchable = true
|
||||
min_selected = 1
|
||||
max_selected = 3
|
||||
required = true
|
||||
options = [
|
||||
{ value = "react", label = "React" },
|
||||
{ value = "vue", label = "Vue" },
|
||||
{ value = "angular", label = "Angular" },
|
||||
{ value = "svelte", label = "Svelte" },
|
||||
{ value = "nextjs", label = "Next.js" },
|
||||
{ value = "nuxt", label = "Nuxt" },
|
||||
{ value = "astro", label = "Astro" },
|
||||
{ value = "remix", label = "Remix" },
|
||||
{ value = "gatsby", label = "Gatsby" },
|
||||
{ value = "qwik", label = "Qwik" },
|
||||
]
|
||||
|
||||
# Example with min/max validation
|
||||
[[elements]]
|
||||
name = "permissions"
|
||||
type = "multiselect"
|
||||
prompt = "User Permissions"
|
||||
help = "Select between 1 and 3 permissions"
|
||||
display_mode = "grid"
|
||||
min_selected = 1
|
||||
max_selected = 3
|
||||
default = "read"
|
||||
options = [
|
||||
{ value = "read", label = "📖 Read" },
|
||||
{ value = "write", label = "✏️ Write" },
|
||||
{ value = "delete", label = "🗑️ Delete" },
|
||||
{ value = "admin", label = "🔑 Admin" },
|
||||
]
|
||||
Loading…
x
Reference in New Issue
Block a user