diff --git a/docs/formBuilder/demos/customOptions.md b/docs/formBuilder/demos/customOptions.md new file mode 100644 index 000000000..a7d828a0c --- /dev/null +++ b/docs/formBuilder/demos/customOptions.md @@ -0,0 +1,28 @@ +# Custom Options + +ControlTypes 'select', 'checkbox-group', 'checkbox', 'radio-group', 'autocomplete' all have the ability to have KeyValue option pairs defined. + +Custom controls can add Options using the `type: 'options'` when defining custom attributes. + +For example: +```javascript +{ + defaultAttrs: { + questions: { + label: 'Questions', + type: 'options', + values: [ + { + "label": "Default Column", + "value": "col1", + "selected": false + } + ], + noSelect: true + } +} +``` + +The ability to pre-select any option can be enabled/disabled using the `noSelect` option flag. + +Multiple Option type attributes can be added to a control. \ No newline at end of file diff --git a/docs/formBuilder/options/onAddOption.md b/docs/formBuilder/options/onAddOption.md index 5ad45c071..23c5d32c8 100644 --- a/docs/formBuilder/options/onAddOption.md +++ b/docs/formBuilder/options/onAddOption.md @@ -4,11 +4,27 @@ transform the optionTemplate by return a modified template using the `onAddOptio ## Usage +```javascript +/** + * Callback + * @param optionTemplate Template for new option, expect to be modified if required and returned + * @param {string} optionTemplate.label Option label + * @param {string} optionTemplate.value Option value + * @param {bool} optionTemplate.selected Mark option as selected + * @param optionInfo Details about the option field + * @param {string} optionInfo.type Control type + * @param {string} optionInfo.index Index of new option + * @param {bool} optionTemplate.isMultiple Does the attribute have multiple selections allowed + * @param {bool} optionTemplate.fieldName Name of the field these options are associated with. + */ +onAddOption: (optionTemplate, optionInfo) +``` + ```javascript const options = { - onAddOption: (optionTemplate, optionIndex) => { - optionTemplate.label = `Option ${optionIndex + 1}` - optionTemplate.value = `option-${optionIndex + 1}` + onAddOption: (optionTemplate, optionInfo) => { + optionTemplate.label = `Option ${optionInfo.index + 1}` + optionTemplate.value = `option-${optionInfo.index + 1}` return optionTemplate }, } @@ -19,10 +35,10 @@ To add additional data with specific options, for example, to use with business ```javascript const options = { - onAddOption: (optionTemplate, optionIndex) => { - optionTemplate.label = `Option ${optionIndex.index + 1}` - optionTemplate.value = `option-${optionIndex.index + 1}` - optionTemplate.minLevel = `min-level-${optionIndex.index + 1}` + onAddOption: (optionTemplate, optionInfo) => { + optionTemplate.label = `Option ${optionInfo.index + 1}` + optionTemplate.value = `option-${optionInfo.index + 1}` + optionTemplate.minLevel = `min-level-${optionInfo.index + 1}` return optionTemplate } diff --git a/package-lock.json b/package-lock.json index e3f65a60c..a7d3d39fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,7 +52,8 @@ "inquirer": "^7.3.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "jest-scss-transform": "github:routablehq/jest-scss-transform#6e3f5974", + jest-scss-transform, + "jest-scss-transform": "^1.0.4", "lint-staged": "^15.2.0", "mi18n": "^0.4.8", "npm-run-all": "^4.1.5", @@ -66,7 +67,7 @@ "storage-available": "^1.1.0", "style-loader": "^1.2.1", "unzipper": "^0.12.1", - "webpack": "^5.88.0", + "webpack": "^5.94.0", "webpack-bundle-analyzer": "^4.9.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.0", @@ -177,9 +178,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.7.tgz", - "integrity": "sha512-SO5E3bVxDuxyNxM5agFv480YA2HO6ohZbGxbazZdIk3KQOPOGVNw6q78I9/lbviIf95eq6tPozeYnJLbjnC8IA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.8.tgz", + "integrity": "sha512-Po3VLMN7fJtv0nsOjBDSbO1J71UhzShE9MuOSkWEV9IZQXzhZklYtzKZ8ZD/Ij3a0JBv1AG3Ny2L3jvAHQVOGg==", "dev": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", @@ -2516,18 +2517,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz", - "integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", "dev": true, "dependencies": { "@eslint/object-schema": "^2.1.4", @@ -2538,6 +2539,15 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", + "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", @@ -2596,9 +2606,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz", - "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==", + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", + "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2613,6 +2623,40 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", + "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", + "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", + "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.0", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2627,9 +2671,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, "engines": { "node": ">=18.18" @@ -3936,18 +3980,6 @@ "node": ">=18" } }, - "node_modules/@semantic-release/github/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/@semantic-release/github/node_modules/aggregate-error": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", @@ -4011,32 +4043,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/github/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@semantic-release/github/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/@semantic-release/github/node_modules/indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", @@ -4503,20 +4509,10 @@ "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/express": { @@ -5025,15 +5021,15 @@ } }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { - "debug": "4" + "debug": "^4.3.4" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/aggregate-error": { @@ -5729,9 +5725,9 @@ } }, "node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "node_modules/body-parser": { @@ -7174,24 +7170,6 @@ "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", "dev": true }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, "node_modules/d": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", @@ -7210,20 +7188,6 @@ "node": ">=8" } }, - "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -7718,9 +7682,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -8176,27 +8140,31 @@ } }, "node_modules/eslint": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.6.0.tgz", - "integrity": "sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==", + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", + "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/config-array": "^0.17.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.6.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.6.0", + "@eslint/js": "9.12.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.1", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -8206,14 +8174,11 @@ "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { @@ -8224,6 +8189,14 @@ }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -8239,13 +8212,13 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -8281,18 +8254,6 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint-webpack-plugin": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-4.2.0.tgz", @@ -8414,9 +8375,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", - "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -8429,6 +8390,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -8459,15 +8432,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -8515,14 +8479,14 @@ } }, "node_modules/espree": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", "dev": true, "dependencies": { "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" + "eslint-visitor-keys": "^4.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8531,6 +8495,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -9633,9 +9609,9 @@ } }, "node_modules/globals": { - "version": "15.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.7.0.tgz", - "integrity": "sha512-ivatRXWwKC6ImcdKO7dOwXuXR5XFrdwo45qFwD7D0qOkEPzzJdLXC3BHceBdyrPOD3p1suPaWi4Y4NMm2D++AQ==", + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", + "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", "dev": true, "engines": { "node": ">=18" @@ -9901,18 +9877,6 @@ "wbuf": "^1.1.0" } }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/html-entities": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", @@ -10081,17 +10045,16 @@ } }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/http-proxy-middleware": { @@ -10125,16 +10088,16 @@ "dev": true }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/human-signals": { @@ -11667,6 +11630,213 @@ } } }, + "node_modules/jest-environment-jsdom/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom/node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/jest-environment-jsdom/node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-jsdom/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jest-environment-jsdom/node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", @@ -12739,51 +12909,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -17525,9 +17650,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", - "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz", + "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==", "dev": true }, "node_modules/object-assign": { @@ -20525,9 +20650,9 @@ "dev": true }, "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, "dependencies": { "@pkgr/core": "^0.1.0", @@ -20906,18 +21031,6 @@ "node": ">= 4.0.0" } }, - "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/traverse": { "version": "0.6.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", @@ -21235,12 +21348,12 @@ } }, "node_modules/unzipper": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.1.tgz", - "integrity": "sha512-wjYe5XddA387WIAZbEMWOT0U8kw9yf1MdfLHccJer1y7a80t3DqVv0SHOAWV5NDBD2TUPj/pFYmK9tCeY6l9UQ==", + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.3.tgz", + "integrity": "sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA==", "dev": true, "dependencies": { - "bluebird": "~3.4.1", + "bluebird": "~3.7.2", "duplexer2": "~0.1.4", "fs-extra": "^11.2.0", "graceful-fs": "^4.2.2", @@ -21394,18 +21507,6 @@ "node": ">= 0.8" } }, - "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dev": true, - "dependencies": { - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -21447,12 +21548,11 @@ } }, "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.95.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", + "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -21461,7 +21561,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -21855,52 +21955,6 @@ "node": ">=0.8.0" } }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -22080,9 +22134,9 @@ } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "engines": { "node": ">=10.0.0" @@ -22100,15 +22154,6 @@ } } }, - "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", diff --git a/package.json b/package.json index 77d8a4902..6156c2309 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "inquirer": "^7.3.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "jest-scss-transform": "github:routablehq/jest-scss-transform#6e3f5974", + "jest-scss-transform": "^1.0.4", "lint-staged": "^15.2.0", "mi18n": "^0.4.8", "npm-run-all": "^4.1.5", @@ -161,7 +161,7 @@ "storage-available": "^1.1.0", "style-loader": "^1.2.1", "unzipper": "^0.12.1", - "webpack": "^5.88.0", + "webpack": "^5.94.0", "webpack-bundle-analyzer": "^4.9.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.0", @@ -233,4 +233,4 @@ ] } } -} +} \ No newline at end of file diff --git a/src/js/form-builder.js b/src/js/form-builder.js index b52902da1..cc1750304 100644 --- a/src/js/form-builder.js +++ b/src/js/form-builder.js @@ -385,14 +385,17 @@ function FormBuilder(opts, element, $) { /** * Add data for field with options [select, checkbox-group, radio-group] * + * @param {string} fieldName * @param {Object} fieldData * @return {string} field options markup */ - const fieldOptions = function (fieldData) { - const { type, values } = fieldData + const fieldOptions = function (fieldName, fieldData) { + const { type } = fieldData let fieldValues const optionActions = [m('a', mi18n.get('addOption'), { className: 'add add-opt' })] - const fieldOptions = [m('label', mi18n.get('selectOptions'), { className: 'false-label' })] + const fieldLabel = fieldName === 'values' ? mi18n.get('selectOptions') : i18n[fieldName] + const fieldOptions = [m('label', fieldLabel, { className: 'false-label' })] + const optionsNoSelect = fieldData['noSelect'] ?? false const isMultiple = fieldData.multiple || type === 'checkbox-group' const optionDataTemplate = count => { const label = mi18n.get('optionCount', count) @@ -403,6 +406,11 @@ function FormBuilder(opts, element, $) { } } + /** + * For build-in options attributes the options are stored in the "values" key + * For custom options the options will be in the attribute named key when in saved formData or in the values key when coming from field definition + */ + const values = fieldName === 'options' ? fieldData['values'] : (fieldData[fieldName] || fieldData['values']) if (!values || !values.length) { let defaultOptCount = [1, 2, 3] if (['checkbox-group', 'checkbox'].includes(type)) { @@ -415,27 +423,29 @@ function FormBuilder(opts, element, $) { firstOption.selected = true } } else { - // ensure option data is has all required keys + // ensure option data contains all required keys fieldValues = values.map(option => Object.assign({}, { selected: false }, option)) } - const optionActionsWrap = m('div', optionActions, { className: 'option-actions' }) + const optionGroupName = window.crypto.randomUUID() + '-options' const options = m( 'ol', - fieldValues.map((option, index, _, fieldName = fieldData.name) => { - const optionData = config.opts.onAddOption(option, { type, index, isMultiple }) - const optionGroupName = fieldName + '-options' + fieldValues.map((option, index) => { + const optionData = config.opts.onAddOption(option, { type, index, isMultiple, fieldName }) + if (optionsNoSelect) { + optionData.selected = false + } return selectFieldOptions(optionGroupName, optionData, isMultiple) }), { - className: 'sortable-options', + className: 'sortable-options' + (optionsNoSelect ? ' options-no-select' : ''), }, ) const optionsWrap = m('div', [options, optionActionsWrap], { className: 'sortable-options-wrap' }) fieldOptions.push(optionsWrap) - return m('div', fieldOptions, { className: 'form-group field-options' }).outerHTML + return m('div', fieldOptions, { name: fieldName, className: 'form-group field-options' }).outerHTML } const defaultFieldAttrs = type => { @@ -562,7 +572,7 @@ function FormBuilder(opts, element, $) { first: mi18n.get('enableOther'), second: mi18n.get('enableOtherMsg'), }), - options: () => fieldOptions(values), + options: () => fieldOptions('values', values), requireValidOption: () => boolAttribute('requireValidOption', values, { first: ' ', @@ -655,11 +665,14 @@ function FormBuilder(opts, element, $) { * @return {string} type of user attr */ function userAttrType(attrData) { - return [ - ['array', ({ options }) => !!options], - ['boolean', ({ type }) => type === 'checkbox'], // automatic bool if checkbox - [typeof attrData.value, () => true], // string, number, - ].find(typeCondition => typeCondition[1](attrData))[0] + return ( + [ + ['array', ({ options }) => !!options], + ['boolean', ({ type }) => type === 'checkbox'], // automatic bool if checkbox + ['options', ({ type }) => type === 'options'], + [typeof attrData.value, () => true], // string, number, + ].find(typeCondition => typeCondition[1](attrData))[0] + ) } /** @@ -695,6 +708,7 @@ function FormBuilder(opts, element, $) { } return boolAttribute(attr, { ...attrData, [attr]: isChecked }, { first: i18n[attr] }) }, + options: fieldOptions, } for (const attribute in typeUserAttr) { @@ -1714,7 +1728,7 @@ function FormBuilder(opts, element, $) { $stage.on('dblclick', 'li.form-field', e => { if ( - ['select', 'input', 'label', 'textarea'].includes(e.target.tagName.toLowerCase()) || + ['select', 'input', 'label', 'textarea', 'a',].includes(e.target.tagName.toLowerCase()) || e.target.isContentEditable === true ) { return @@ -2391,6 +2405,7 @@ function FormBuilder(opts, element, $) { $stage.on('click', '.add-opt', function (e) { e.preventDefault() const type = $(e.target).closest('.form-field').attr('type') + const fieldName = $(e.target).closest('.form-group.field-options').attr('name') const $optionWrap = $(e.target).closest('.field-options') const $multiple = $('[name="multiple"]', $optionWrap) const $firstOption = $('.option-selected:eq(0)', $optionWrap) @@ -2402,6 +2417,7 @@ function FormBuilder(opts, element, $) { type, index: $sortableOptions.children().length, isMultiple, + fieldName, }) $sortableOptions.append(selectFieldOptions($firstOption.attr('name'), optionData, isMultiple)) }) diff --git a/src/js/form-render.js b/src/js/form-render.js index 8331960eb..a50892a09 100644 --- a/src/js/form-render.js +++ b/src/js/form-render.js @@ -290,12 +290,9 @@ class FormRender { const userDataMap = $('select, input, textarea', container) .serializeArray() .reduce((acc, { name, value }) => { - name = name.replace('[]', '') - if (acc[name]) { - acc[name].push(value) - } else { - acc[name] = [value] - } + name = name.replace(/\[\w*]/, '') + acc[name] ??= [] + acc[name].push(value) return acc }, {}) diff --git a/src/js/helpers.js b/src/js/helpers.js index d5c207f47..03d610ac4 100644 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -199,7 +199,6 @@ export default class Helpers { */ prepData(form) { const formData = [] - const d = this.d const _this = this const config = this.config @@ -283,11 +282,10 @@ export default class Helpers { fieldData = trimObj(fieldData) - const multipleField = fieldData.type && fieldData.type.match(d.optionFieldsRegEx) - - if (multipleField) { - fieldData.values = _this.fieldOptionData($field) - } + $field.find('.form-group.field-options').each((_, attribute) => { + const attributeName = attribute.getAttribute('name') + fieldData[attributeName] = _this.fieldOptionData(attribute) + }) formData.push(fieldData) } @@ -397,7 +395,6 @@ export default class Helpers { */ updatePreview($field) { const _this = this - const d = this.d const fieldClass = $field.attr('class') const field = $field[0] if (fieldClass.includes('input-control')) { @@ -408,19 +405,10 @@ export default class Helpers { const $prevHolder = $('.prev-holder', field) let previewData = Object.assign({}, _this.getAttrVals(field), { type: fieldType }) - if (fieldType.match(d.optionFieldsRegEx)) { - previewData.values = [] - previewData.multiple = $('[name="multiple"]', field).is(':checked') - - $('.sortable-options li', field).each(function (i, $option) { - const option = { - selected: $('.option-selected', $option).is(':checked'), - value: $('.option-value', $option).val(), - label: $('.option-label', $option).val(), - } - previewData.values.push(option) - }) - } + $field.find('.form-group.field-options').each((_, attribute) => { + const attributeName = attribute.getAttribute('name') + previewData[attributeName] = _this.fieldOptionData(attribute) + }) previewData = trimObj(previewData, true) @@ -1155,7 +1143,7 @@ export default class Helpers { } /** - * Process user options for actionButtons + * Process user configured options * @param {Object} options * @return {Object} processedOptions */ diff --git a/src/sass/_stage.scss b/src/sass/_stage.scss index 382560f3d..294fbb9c7 100644 --- a/src/sass/_stage.scss +++ b/src/sass/_stage.scss @@ -193,7 +193,7 @@ } .field-label { - display: block; + display: inline; overflow-wrap: break-word; } @@ -468,6 +468,9 @@ padding: 0; > li { + display: flex!important; + flex-wrap: wrap; + cursor: move; margin: 1px; @@ -475,20 +478,21 @@ background-color: $white; &:nth-child(1) .remove { - display: none; + opacity: 0; + pointer-events: none; } .remove { - position: relative; + flex: none; + display: flex; + align-items: center; + justify-content: center; opacity: 1; - float: right; - right: 14px; - height: 18px; width: 18px; - top: 8px; font-size: 12px; padding: 0; color: $error; + margin-left: 3px; &::before { margin: 0; } @@ -502,12 +506,15 @@ } .option-selected { - margin: 0; - width: 5%; + margin: 0 3px 0 0; + flex: none; } input[type='text'] { - width: calc(44.5% - 17px); + flex-grow: 1; + flex-shrink: 0; + flex-basis: auto; + width: 100px; margin: 0 3px; float: none; } @@ -879,3 +886,7 @@ position: fixed !important; left: -100px !important; } + +ol.options-no-select input[type=radio]{ + display: none; +} \ No newline at end of file diff --git a/tests/control/custom.test.js b/tests/control/custom.test.js index 9ecef5d10..984c563a6 100644 --- a/tests/control/custom.test.js +++ b/tests/control/custom.test.js @@ -1,6 +1,7 @@ require('../setup-fb') require('./../../src/js/form-builder.js') require('./../../src/js/form-render.js') +const {errorHandler} = require('../__mocks__/errorHandlers.js') describe('Test Custom Control', () => { test('test add custom field with template', async () => { @@ -406,6 +407,7 @@ describe('Test Custom Control', () => { }) test('custom replacedField with invalid type throws Error', async () => { + const errors = [], warnings = [], success = [] const fbWrap = $('
') const replaceFields = [{ @@ -413,13 +415,15 @@ describe('Test Custom Control', () => { }] let error - await fbWrap.formBuilder({replaceFields}).promise.catch(e => error = e) + await fbWrap.formBuilder({replaceFields, notify: errorHandler(errors,warnings,success) }).promise.catch(e => error = e) expect(error).toBeInstanceOf(Error) expect(error.message).toMatch(/Error while registering custom field:/) + expect(errors).toHaveLength(1) }) test('custom replacedField with missing type throws Error', async () => { + const errors = [], warnings = [], success = [] const fbWrap = $('
') const replaceFields = [{ @@ -427,9 +431,10 @@ describe('Test Custom Control', () => { }] let error - await fbWrap.formBuilder({replaceFields}).promise.catch(e => error = e) + await fbWrap.formBuilder({replaceFields, notify: errorHandler(errors,warnings,success) }).promise.catch(e => error = e) expect(error).toBeInstanceOf(Error) expect(error.message).toMatch(/Ignoring invalid custom field definition. Please specify a type property./) + expect(errors).toHaveLength(1) }) }) \ No newline at end of file diff --git a/tests/form-builder-custom.test.js b/tests/form-builder-custom.test.js index f55dbe8de..391fd3e9b 100644 --- a/tests/form-builder-custom.test.js +++ b/tests/form-builder-custom.test.js @@ -20,7 +20,18 @@ beforeAll(() => { 'label': 'extracontent', 'value' : '', 'type': 'textarea' - } + }, + my_list: { + label: 'My List', + type: 'options', + values: [ + { + 'label': 'Option 1', + 'value': 'option-1', + }, + ], + noSelect: true, + }, } } } @@ -94,4 +105,26 @@ describe('controlPlugins', () => { expect(fbWrap.find('textarea[name="Extra Content"]')).toHaveLength(1) }) +}) + +describe('CustomControl additional Option type attributes', () => { + test('default rendered options', async () => { + const fbWrap = $('
') + const fb = await $(fbWrap).formBuilder({ }).promise + const field = { + type: 'testPlugin', + class: 'form-control' + } + fb.actions.addField(field) + expect(fbWrap.find('.field-options[name="my_list"]')).toHaveLength(1) + + const defaultOptions = [ + { + 'label': 'Option 1', + 'value': 'option-1', + 'selected': false + }, + ] + expect(fb.actions.getData('js')[0].my_list).toEqual(defaultOptions) + }) }) \ No newline at end of file diff --git a/tests/form-builder.test.js b/tests/form-builder.test.js index 359c6dc4d..2eed21e19 100644 --- a/tests/form-builder.test.js +++ b/tests/form-builder.test.js @@ -17,23 +17,6 @@ describe('FormBuilder Stage Setup', () => { }) describe('FormBuilder Add/Remove from Stage', () => { - test('fieldAdded callback called after adding field', async () => { - const fbWrap = $('
') - const cb = jest.fn() - const fb = await $(fbWrap).formBuilder({'onAddField': cb}).promise - const field = { - type: 'text', - class: 'form-control', - name: 'on-add-test' - } - fb.actions.addField(field) - expect(cb.mock.calls).toHaveLength(1) - expect(cb.mock.calls[0]).toHaveLength(2) - expect(typeof cb.mock.calls[0][0]).toBe('string') - expect(typeof cb.mock.calls[0][1]).toBe('object') - expect(cb.mock.calls[0][1].name).toBe('on-add-test') - }) - test('fieldsRemovedOnClean', async() => { const config = { formData: [ @@ -809,4 +792,117 @@ describe('FormBuilder disabling attributes', () => { expect(fbWrap.find('.form-field[type="textarea"] .label-wrap')).toHaveLength(1) expect(fbWrap.find('.form-field[type="textarea"] .description-wrap')).toHaveLength(1) }) +}) + +describe('FormBuilder option attributes', () => { + test('default rendered options', async () => { + const fbWrap = $('
') + const fb = await $(fbWrap).formBuilder({ }).promise + const field = { + type: 'select', + class: 'form-control' + } + fb.actions.addField(field) + expect(fbWrap.find('.field-options')).toHaveLength(1) + + const defaultOptions = [ + { + 'label': 'Option 1', + 'value': 'option-1', + 'selected': true + }, + { + 'label': 'Option 2', + 'value': 'option-2', + 'selected': false + }, + { + 'label': 'Option 3', + 'value': 'option-3', + 'selected': false + } + ] + expect(fb.actions.getData('js')[0].values).toEqual(defaultOptions) + }) +}) + +describe('FormBuilder callbacks', () => { + test('onAddField callback called after adding field', async () => { + const fbWrap = $('
') + const cb = jest.fn() + const fb = await $(fbWrap).formBuilder({'onAddField': cb}).promise + const field = { + type: 'text', + class: 'form-control', + name: 'on-add-test' + } + fb.actions.addField(field) + expect(cb.mock.calls).toHaveLength(1) + expect(cb.mock.calls[0]).toHaveLength(2) + expect(typeof cb.mock.calls[0][0]).toBe('string') + expect(typeof cb.mock.calls[0][1]).toBe('object') + expect(cb.mock.calls[0][1].name).toBe('on-add-test') + }) + + test('onAddFieldAfter callback called after adding field', async () => { + const fbWrap = $('
') + const cb = jest.fn() + const fb = await $(fbWrap).formBuilder({'onAddFieldAfter': cb}).promise + const field = { + type: 'text', + class: 'form-control', + name: 'on-add-test' + } + fb.actions.addField(field) + expect(cb.mock.calls).toHaveLength(1) + expect(cb.mock.calls[0]).toHaveLength(2) + expect(typeof cb.mock.calls[0][0]).toBe('string') + expect(typeof cb.mock.calls[0][1]).toBe('object') + expect(cb.mock.calls[0][1].name).toBe('on-add-test') + }) + + test.failing('onSave callback called after save', async () => { + const fbWrap = $('
') + const cb = jest.fn() + const fb = await $(fbWrap).formBuilder({'onSave': cb}).promise + + //Calling Save action + fb.actions.save() + expect(cb.mock.calls).toHaveLength(1) + expect(cb.mock.calls[0]).toHaveLength(1) + expect(cb.mock.calls[0][0]).toBeUndefined() + expect(typeof cb.mock.calls[0][1]).toBe('object') + expect(cb.mock.calls[0][1]).toHaveLength(0) + + const field = { + type: 'text', + class: 'form-control', + } + fb.actions.addField(field) + $('button.save-template', fbWrap).click() + expect(cb.mock.calls).toHaveLength(2) + expect(cb.mock.calls[1]).toHaveLength(2) + expect(typeof cb.mock.calls[1][0]).toBe('object') + expect(typeof cb.mock.calls[1][1]).toBe('object') + expect(cb.mock.calls[1][1]).toHaveLength(1) + }) + + test('onAddOption callback called after clicking .add-opt', async () => { + const fbWrap = $('
') + const cb = jest.fn() + const fb = await $(fbWrap).formBuilder({'onAddOption': cb}).promise + + const field = { + type: 'select', + class: 'form-control', + name: 'on-add-test' + } + fb.actions.addField(field) + + expect(cb.mock.calls).toHaveLength(3) + expect(typeof cb.mock.calls[0][0]).toBe('object') + expect(typeof cb.mock.calls[0][1]).toBe('object') + expect(Object.keys(cb.mock.calls[0][0])).toHaveLength(3) + expect(Object.keys(cb.mock.calls[0][1])).toHaveLength(4) + }) }) \ No newline at end of file diff --git a/tests/setup-jest.js b/tests/setup-jest.js index 81f84b0d7..6c525939b 100644 --- a/tests/setup-jest.js +++ b/tests/setup-jest.js @@ -17,3 +17,9 @@ console.error = (...params) => { originalConsoleError(...params) } } + +import { webcrypto } from 'node:crypto' + +Object.defineProperty(global, 'crypto', { + value: webcrypto, +}) \ No newline at end of file