diff --git a/package.json b/package.json index 9c8018a9..86473b9d 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "license": "MIT", "private": false, - "version": "0.12.32", + "version": "0.13.0", "type": "module", "files": [ "dist", @@ -81,7 +81,8 @@ "awesome-debounce-promise": "^2.1.0", "classnames": "^2.5.1", "css-vars-hook": "^0.7.6", - "the-new-css-reset": "^1.11.2" + "the-new-css-reset": "^1.11.2", + "yup": "^1.4.0" }, "devDependencies": { "@commitlint/cli": "19.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50fb4d95..a2c3e7ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: the-new-css-reset: specifier: ^1.11.2 version: 1.11.2 + yup: + specifier: ^1.4.0 + version: 1.4.0 devDependencies: '@commitlint/cli': specifier: 19.3.0 @@ -122,7 +125,7 @@ importers: version: 1.0.1 alias-hq: specifier: 6.2.3 - version: 6.2.3(@babel/preset-env@7.24.6(@babel/core@7.24.6)) + version: 6.2.3(@babel/preset-env@7.24.6(@babel/core@7.24.3)) commitizen: specifier: 4.3.0 version: 4.3.0(@types/node@20.14.9)(typescript@5.4.5) @@ -6524,6 +6527,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-expr@2.0.6: + resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -7340,6 +7346,9 @@ packages: resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} engines: {node: '>=12'} + tiny-case@1.0.3: + resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -7381,6 +7390,9 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + tough-cookie@4.1.3: resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} engines: {node: '>=6'} @@ -7958,6 +7970,9 @@ packages: resolution: {integrity: sha512-Ct97huExsu7cWeEjmrXlofevF8CvzUglJ4iGUet5B8xn1oumtAZBpHU4GzYuoE6PVqcZ5hghtBrSlhwHuR1Jmw==} engines: {node: '>=18'} + yup@1.4.0: + resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==} + z-schema@5.0.5: resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} engines: {node: '>=8.0.0'} @@ -8095,29 +8110,29 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.6 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.24.6(@babel/core@7.24.3)': + '@babel/helper-create-class-features-plugin@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-annotate-as-pure': 7.24.6 '@babel/helper-environment-visitor': 7.24.6 '@babel/helper-function-name': 7.24.6 '@babel/helper-member-expression-to-functions': 7.24.6 '@babel/helper-optimise-call-expression': 7.24.6 - '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.3) + '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6) '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 '@babel/helper-split-export-declaration': 7.24.6 semver: 6.3.1 - '@babel/helper-create-regexp-features-plugin@7.24.6(@babel/core@7.24.3)': + '@babel/helper-create-regexp-features-plugin@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-annotate-as-pure': 7.24.6 regexpu-core: 5.3.2 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.1(@babel/core@7.24.3)': + '@babel/helper-define-polyfill-provider@0.6.1(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-compilation-targets': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 debug: 4.3.4 @@ -8171,15 +8186,6 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.6 '@babel/helper-validator-identifier': 7.24.6 - '@babel/helper-module-transforms@7.24.6(@babel/core@7.24.3)': - dependencies: - '@babel/core': 7.24.3 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-module-imports': 7.24.6 - '@babel/helper-simple-access': 7.24.6 - '@babel/helper-split-export-declaration': 7.24.6 - '@babel/helper-validator-identifier': 7.24.6 - '@babel/helper-module-transforms@7.24.6(@babel/core@7.24.6)': dependencies: '@babel/core': 7.24.6 @@ -8201,9 +8207,9 @@ snapshots: '@babel/helper-plugin-utils@7.24.6': {} - '@babel/helper-remap-async-to-generator@7.24.6(@babel/core@7.24.3)': + '@babel/helper-remap-async-to-generator@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-annotate-as-pure': 7.24.6 '@babel/helper-environment-visitor': 7.24.6 '@babel/helper-wrap-function': 7.24.6 @@ -8222,9 +8228,9 @@ snapshots: '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers@7.24.6(@babel/core@7.24.3)': + '@babel/helper-replace-supers@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-environment-visitor': 7.24.6 '@babel/helper-member-expression-to-functions': 7.24.6 '@babel/helper-optimise-call-expression': 7.24.6 @@ -8291,27 +8297,27 @@ snapshots: dependencies: '@babel/types': 7.24.6 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-environment-visitor': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 - '@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.3) + '@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.6) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-environment-visitor': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 @@ -8334,14 +8340,15 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.3)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.3)': dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.6)': dependencies: @@ -8363,25 +8370,26 @@ snapshots: dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.6)': dependencies: '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.3)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.3)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.3)': + '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-syntax-flow@7.24.1(@babel/core@7.24.3)': @@ -8394,20 +8402,21 @@ snapshots: '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-import-assertions@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-syntax-import-assertions@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-import-attributes@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-syntax-import-attributes@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.3)': dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.6)': dependencies: @@ -8418,6 +8427,7 @@ snapshots: dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.6)': dependencies: @@ -8438,6 +8448,7 @@ snapshots: dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.6)': dependencies: @@ -8458,6 +8469,7 @@ snapshots: dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.6)': dependencies: @@ -8468,6 +8480,7 @@ snapshots: dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.6)': dependencies: @@ -8478,6 +8491,7 @@ snapshots: dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.6)': dependencies: @@ -8494,15 +8508,16 @@ snapshots: '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.3)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.3)': dependencies: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.6 + optional: true '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.6)': dependencies: @@ -8519,112 +8534,106 @@ snapshots: '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.3)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-arrow-functions@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-arrow-functions@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-async-generator-functions@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-async-generator-functions@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-environment-visitor': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.3) + '@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.6) - '@babel/plugin-transform-async-to-generator@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-async-to-generator@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-module-imports': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.3) + '@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.6) - '@babel/plugin-transform-block-scoped-functions@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-block-scoped-functions@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 - - '@babel/plugin-transform-block-scoping@7.24.6(@babel/core@7.24.3)': - dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-class-properties@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-block-scoping@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-transform-class-properties@7.24.6(@babel/core@7.24.6)': dependencies: '@babel/core': 7.24.6 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-class-static-block@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-class-static-block@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.3) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.6) - '@babel/plugin-transform-classes@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-classes@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-annotate-as-pure': 7.24.6 '@babel/helper-compilation-targets': 7.24.6 '@babel/helper-environment-visitor': 7.24.6 '@babel/helper-function-name': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.3) + '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6) '@babel/helper-split-export-declaration': 7.24.6 globals: 11.12.0 - '@babel/plugin-transform-computed-properties@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-computed-properties@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/template': 7.24.6 - '@babel/plugin-transform-destructuring@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-destructuring@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-dotall-regex@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-dotall-regex@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-duplicate-keys@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-duplicate-keys@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-dynamic-import@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-dynamic-import@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.3) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.6) - '@babel/plugin-transform-exponentiation-operator@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-exponentiation-operator@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-export-namespace-from@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-export-namespace-from@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.3) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.6) '@babel/plugin-transform-flow-strip-types@7.24.1(@babel/core@7.24.3)': dependencies: @@ -8638,45 +8647,45 @@ snapshots: '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-syntax-flow': 7.24.1(@babel/core@7.24.6) - '@babel/plugin-transform-for-of@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-for-of@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 - '@babel/plugin-transform-function-name@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-function-name@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-compilation-targets': 7.24.6 '@babel/helper-function-name': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-json-strings@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-json-strings@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.6) - '@babel/plugin-transform-literals@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-literals@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-logical-assignment-operators@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-logical-assignment-operators@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.6) - '@babel/plugin-transform-member-expression-literals@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-member-expression-literals@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-modules-amd@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-modules-amd@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-transform-modules-commonjs@7.24.1(@babel/core@7.24.3)': @@ -8693,125 +8702,99 @@ snapshots: '@babel/helper-plugin-utils': 7.24.0 '@babel/helper-simple-access': 7.22.5 - '@babel/plugin-transform-modules-commonjs@7.24.6(@babel/core@7.24.3)': - dependencies: - '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-simple-access': 7.24.6 - '@babel/plugin-transform-modules-commonjs@7.24.6(@babel/core@7.24.6)': dependencies: '@babel/core': 7.24.6 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 '@babel/helper-simple-access': 7.24.6 - '@babel/plugin-transform-modules-systemjs@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-modules-systemjs@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-hoist-variables': 7.24.6 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 '@babel/helper-validator-identifier': 7.24.6 - '@babel/plugin-transform-modules-umd@7.24.6(@babel/core@7.24.3)': - dependencies: - '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 - - '@babel/plugin-transform-named-capturing-groups-regex@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-modules-umd@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-new-target@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-named-capturing-groups-regex@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-nullish-coalescing-operator@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-new-target@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.3) '@babel/plugin-transform-nullish-coalescing-operator@7.24.6(@babel/core@7.24.6)': dependencies: '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.6) - '@babel/plugin-transform-numeric-separator@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-numeric-separator@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.6) - '@babel/plugin-transform-object-rest-spread@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-object-rest-spread@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-compilation-targets': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.3) - - '@babel/plugin-transform-object-super@7.24.6(@babel/core@7.24.3)': - dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.6) - '@babel/plugin-transform-optional-catch-binding@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-object-super@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.3) + '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6) - '@babel/plugin-transform-optional-chaining@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-optional-catch-binding@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.6) '@babel/plugin-transform-optional-chaining@7.24.6(@babel/core@7.24.6)': dependencies: '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.3) - - '@babel/plugin-transform-parameters@7.24.6(@babel/core@7.24.3)': - dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.6) - '@babel/plugin-transform-private-methods@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-parameters@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-transform-private-methods@7.24.6(@babel/core@7.24.6)': dependencies: '@babel/core': 7.24.6 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-private-property-in-object@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-private-property-in-object@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-annotate-as-pure': 7.24.6 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.3) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.6) - '@babel/plugin-transform-property-literals@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-property-literals@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-transform-react-jsx-self@7.24.6(@babel/core@7.24.6)': @@ -8824,41 +8807,41 @@ snapshots: '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-regenerator@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-regenerator@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 regenerator-transform: 0.15.2 - '@babel/plugin-transform-reserved-words@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-reserved-words@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-shorthand-properties@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-shorthand-properties@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-spread@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-spread@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 - '@babel/plugin-transform-sticky-regex@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-sticky-regex@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-template-literals@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-template-literals@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-typeof-symbol@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-typeof-symbol@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-transform-typescript@7.24.1(@babel/core@7.24.3)': @@ -8877,111 +8860,111 @@ snapshots: '@babel/helper-plugin-utils': 7.24.6 '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.6) - '@babel/plugin-transform-unicode-escapes@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-unicode-escapes@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-unicode-property-regex@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-unicode-property-regex@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-unicode-regex@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-unicode-regex@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/plugin-transform-unicode-sets-regex@7.24.6(@babel/core@7.24.3)': + '@babel/plugin-transform-unicode-sets-regex@7.24.6(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) '@babel/helper-plugin-utils': 7.24.6 - '@babel/preset-env@7.24.6(@babel/core@7.24.3)': + '@babel/preset-env@7.24.6(@babel/core@7.24.6)': dependencies: '@babel/compat-data': 7.24.6 - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-compilation-targets': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/helper-validator-option': 7.24.6 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.3) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.3) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.3) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.3) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-import-assertions': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-syntax-import-attributes': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.3) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.3) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.3) - '@babel/plugin-transform-arrow-functions': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-async-generator-functions': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-async-to-generator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-block-scoped-functions': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-block-scoping': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-class-properties': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-class-static-block': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-classes': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-computed-properties': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-destructuring': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-dotall-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-duplicate-keys': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-dynamic-import': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-exponentiation-operator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-export-namespace-from': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-for-of': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-function-name': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-json-strings': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-literals': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-logical-assignment-operators': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-member-expression-literals': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-modules-amd': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-modules-commonjs': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-modules-systemjs': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-modules-umd': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-named-capturing-groups-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-new-target': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-nullish-coalescing-operator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-numeric-separator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-object-rest-spread': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-object-super': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-optional-catch-binding': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-private-methods': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-private-property-in-object': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-property-literals': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-regenerator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-reserved-words': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-shorthand-properties': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-spread': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-sticky-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-template-literals': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-typeof-symbol': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-escapes': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-property-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-sets-regex': 7.24.6(@babel/core@7.24.3) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.3) - babel-plugin-polyfill-corejs2: 0.4.10(@babel/core@7.24.3) - babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.3) - babel-plugin-polyfill-regenerator: 0.6.1(@babel/core@7.24.3) + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.6) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.6) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.6) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.6) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-import-assertions': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-syntax-import-attributes': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.6) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.6) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.6) + '@babel/plugin-transform-arrow-functions': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-async-generator-functions': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-async-to-generator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-block-scoped-functions': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-block-scoping': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-class-properties': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-class-static-block': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-classes': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-computed-properties': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-destructuring': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-dotall-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-duplicate-keys': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-dynamic-import': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-exponentiation-operator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-export-namespace-from': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-for-of': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-function-name': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-json-strings': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-literals': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-logical-assignment-operators': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-member-expression-literals': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-amd': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-commonjs': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-systemjs': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-umd': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-named-capturing-groups-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-new-target': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-numeric-separator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-object-rest-spread': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-object-super': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-optional-catch-binding': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-private-methods': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-private-property-in-object': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-property-literals': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-regenerator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-reserved-words': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-shorthand-properties': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-spread': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-sticky-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-template-literals': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-typeof-symbol': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-unicode-escapes': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-unicode-property-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-unicode-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-unicode-sets-regex': 7.24.6(@babel/core@7.24.6) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.6) + babel-plugin-polyfill-corejs2: 0.4.10(@babel/core@7.24.6) + babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.6) + babel-plugin-polyfill-regenerator: 0.6.1(@babel/core@7.24.6) core-js-compat: 3.36.1 semver: 6.3.1 transitivePeerDependencies: @@ -9001,9 +8984,9 @@ snapshots: '@babel/helper-validator-option': 7.23.5 '@babel/plugin-transform-flow-strip-types': 7.24.1(@babel/core@7.24.6) - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.3)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.6)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 '@babel/types': 7.24.6 esutils: 2.0.3 @@ -10630,7 +10613,7 @@ snapshots: '@storybook/codemod@8.1.11': dependencies: '@babel/core': 7.24.6 - '@babel/preset-env': 7.24.6(@babel/core@7.24.3) + '@babel/preset-env': 7.24.6(@babel/core@7.24.6) '@babel/types': 7.24.6 '@storybook/csf': 0.1.7 '@storybook/csf-tools': 8.1.11 @@ -11604,13 +11587,13 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 - alias-hq@6.2.3(@babel/preset-env@7.24.6(@babel/core@7.24.6)): + alias-hq@6.2.3(@babel/preset-env@7.24.6(@babel/core@7.24.3)): dependencies: colors: 1.4.0 get-tsconfig: 4.7.3 glob: 7.2.3 inquirer: 7.3.3 - jscodeshift: 0.13.1(@babel/preset-env@7.24.6(@babel/core@7.24.6)) + jscodeshift: 0.13.1(@babel/preset-env@7.24.6(@babel/core@7.24.3)) json5: 2.2.3 module-alias: 2.2.3 node-fetch: 2.7.0 @@ -11865,27 +11848,27 @@ snapshots: '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.5 - babel-plugin-polyfill-corejs2@0.4.10(@babel/core@7.24.3): + babel-plugin-polyfill-corejs2@0.4.10(@babel/core@7.24.6): dependencies: '@babel/compat-data': 7.24.6 - '@babel/core': 7.24.3 - '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.6) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.3): + babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.6): dependencies: - '@babel/core': 7.24.3 - '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.6) core-js-compat: 3.36.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.1(@babel/core@7.24.3): + babel-plugin-polyfill-regenerator@0.6.1(@babel/core@7.24.6): dependencies: - '@babel/core': 7.24.3 - '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.3) + '@babel/core': 7.24.6 + '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.6) transitivePeerDependencies: - supports-color @@ -14528,7 +14511,7 @@ snapshots: dependencies: argparse: 2.0.1 - jscodeshift@0.13.1(@babel/preset-env@7.24.6(@babel/core@7.24.6)): + jscodeshift@0.13.1(@babel/preset-env@7.24.6(@babel/core@7.24.3)): dependencies: '@babel/core': 7.24.3 '@babel/parser': 7.24.1 @@ -14536,7 +14519,7 @@ snapshots: '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.3) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.3) '@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.3) - '@babel/preset-env': 7.24.6(@babel/core@7.24.3) + '@babel/preset-env': 7.24.6(@babel/core@7.24.6) '@babel/preset-flow': 7.24.1(@babel/core@7.24.3) '@babel/preset-typescript': 7.24.1(@babel/core@7.24.3) '@babel/register': 7.23.7(@babel/core@7.24.3) @@ -14576,7 +14559,7 @@ snapshots: temp: 0.8.4 write-file-atomic: 2.4.3 optionalDependencies: - '@babel/preset-env': 7.24.6(@babel/core@7.24.3) + '@babel/preset-env': 7.24.6(@babel/core@7.24.6) transitivePeerDependencies: - supports-color @@ -15680,6 +15663,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + property-expr@2.0.6: {} + proto-list@1.2.4: {} proxy-addr@2.0.7: @@ -16671,6 +16656,8 @@ snapshots: dependencies: convert-hrtime: 5.0.0 + tiny-case@1.0.3: {} + tiny-invariant@1.3.3: {} tinyspy@2.2.1: {} @@ -16707,6 +16694,8 @@ snapshots: toidentifier@1.0.1: {} + toposort@2.0.2: {} + tough-cookie@4.1.3: dependencies: psl: 1.9.0 @@ -17280,6 +17269,13 @@ snapshots: yoctocolors@2.0.2: {} + yup@1.4.0: + dependencies: + property-expr: 2.0.6 + tiny-case: 1.0.3 + toposort: 2.0.2 + type-fest: 2.19.0 + z-schema@5.0.5: dependencies: lodash.get: 4.4.2 diff --git a/release.config.cjs b/release.config.cjs index 80040820..f5f1f1ad 100644 --- a/release.config.cjs +++ b/release.config.cjs @@ -7,7 +7,7 @@ module.exports = { plugins: [ '@semantic-release/commit-analyzer', '@semantic-release/release-notes-generator', - // '@semantic-release/npm', + '@semantic-release/npm', [ '@semantic-release/git', { diff --git a/src/internal/inputs/ValidationProps.ts b/src/internal/inputs/ValidationProps.ts index b59c68d4..4e827643 100644 --- a/src/internal/inputs/ValidationProps.ts +++ b/src/internal/inputs/ValidationProps.ts @@ -4,29 +4,34 @@ export enum ValidationState { error = 'error', valid = 'valid', inProgress = 'inProgress', + pristine = 'pristine', } +export type ValidationConfig = { + state: keyof typeof ValidationState; + errorMessage?: string; +}; + +export type ValidatorFn = ( + value: unknown, + validityState: ValidityState, + formState: FormState +) => string | Promise; + export type ValidationProps = { /** Enable to re-run validation when any field in the form changes */ revalidateOnFormChange?: boolean; - /** - * Provide callback function to validate input. - * @see https://koval.support/inputs/input-validation - */ - validatorFn?: ( - value: unknown, - validityState: ValidityState, - formState: FormState - ) => string | Promise; - /** - * Set external validation state for input. NB! On change validation takes preference over this. - * @see ValidationState - */ - validationState?: keyof typeof ValidationState; /** * Provide custom message for external validation errors. Applies only to errors reported by * validationState prop. * @see validationState */ errorMessage?: string; + /** + * Set external validation state for input. Can be a string or a validator function + * @see ValidationState + * @see ValidatorFn + * @see https://koval.support/inputs/input-validation + */ + validation?: keyof typeof ValidationState | ValidatorFn; }; diff --git a/src/internal/inputs/defaultValidator.ts b/src/internal/inputs/defaultValidator.ts index f2e9734c..967792c6 100644 --- a/src/internal/inputs/defaultValidator.ts +++ b/src/internal/inputs/defaultValidator.ts @@ -1,3 +1,3 @@ -import type {ValidationProps} from './ValidationProps.ts'; +import type {ValidatorFn} from './ValidationProps.ts'; -export const defaultValidator: ValidationProps['validatorFn'] = (_: unknown | undefined) => ''; +export const defaultValidator: ValidatorFn = (_: unknown | undefined) => ''; diff --git a/src/internal/inputs/index.ts b/src/internal/inputs/index.ts index d47a789c..c82488c4 100644 --- a/src/internal/inputs/index.ts +++ b/src/internal/inputs/index.ts @@ -18,3 +18,4 @@ export { export {useHandleFormReset} from './useHandleFormReset.ts'; export {useRevalidateOnFormChange} from './useRevalidateOnFormChange.ts'; export {useExternalValidation} from './useExternalValidation.ts'; +export {useValidationIcon} from './useValidationIcon.tsx'; diff --git a/src/internal/inputs/storybook/patternControl.ts b/src/internal/inputs/storybook/patternControl.ts new file mode 100644 index 00000000..ef1db779 --- /dev/null +++ b/src/internal/inputs/storybook/patternControl.ts @@ -0,0 +1,15 @@ +export const patternControl = { + options: ['noPattern', 'withPattern'], // An array of serializable values + mapping: { + noPattern: undefined, + withPattern: '[^@\\s]+@[^@\\s]+', + }, // Maps serializable option values to complex arg values + control: { + type: 'radio', // Type 'select' is automatically inferred when 'options' is defined + labels: { + // 'labels' maps option values to string labels + noPattern: 'No pattern', + withPattern: 'With pattern ([^@\\s]+@[^@\\s]+)', + }, + }, +}; diff --git a/src/internal/inputs/storybook/prefixControl.ts b/src/internal/inputs/storybook/prefixControl.ts new file mode 100644 index 00000000..a7c6fc76 --- /dev/null +++ b/src/internal/inputs/storybook/prefixControl.ts @@ -0,0 +1,17 @@ +import {CloudUpload} from '@/internal/Icons'; + +export const prefixControl = { + options: ['noPrefix', 'withPrefix'], + mapping: { + noPrefix: undefined, + withPrefix: CloudUpload, + }, + control: { + type: 'radio', + labels: { + // 'labels' maps option values to string labels + noPrefix: 'No prefix', + withPrefix: 'With prefix', + }, + }, +}; diff --git a/src/internal/inputs/storybook/validationControl.ts b/src/internal/inputs/storybook/validationControl.ts new file mode 100644 index 00000000..536f0b44 --- /dev/null +++ b/src/internal/inputs/storybook/validationControl.ts @@ -0,0 +1,123 @@ +import { + timeout, + validatorAsync, + validatorAsyncBoolean, + validatorSync, + validatorSyncBoolean, +} from './../validatorMocks.ts'; +import {ValidationState} from './../ValidationProps.ts'; + +export const validationControl = { + options: [ + 'noValidator', + 'syncValidator', + 'asyncValidator', + 'error', + 'valid', + 'inProgress', + 'pristine', + ], + mapping: { + noValidator: undefined, + syncValidator: validatorSync, + asyncValidator: validatorAsync, + error: ValidationState.error, + valid: ValidationState.valid, + inProgress: ValidationState.inProgress, + pristine: ValidationState.pristine, + }, + control: { + type: 'radio', + labels: { + noValidator: 'No custom validator', + syncValidator: 'Sync validator (value.length < 4)', + asyncValidator: 'Async validator (value.length < 4)', + error: 'External validation: "error"', + valid: 'External validation: "valid"', + inProgress: 'External validation: "inProgress"', + pristine: 'External validation: "pristine"', + }, + }, +}; + +export const validationControlBoolean = { + options: [ + 'noValidator', + 'syncValidator', + 'asyncValidator', + 'error', + 'valid', + 'inProgress', + 'pristine', + ], // An array of serializable values + mapping: { + noValidator: undefined, + syncValidator: validatorSyncBoolean, + asyncValidator: validatorAsyncBoolean, + error: ValidationState.error, + valid: ValidationState.valid, + inProgress: ValidationState.inProgress, + pristine: ValidationState.pristine, + }, // Maps serializable option values to complex arg values + control: { + type: 'radio', // Type 'select' is automatically inferred when 'options' is defined + labels: { + // 'labels' maps option values to string labels + noValidator: 'No custom validator', + syncValidator: 'Sync validator (value !== true)', + asyncValidator: 'Async validator (value !== true)', + error: 'External validation: "error"', + valid: 'External validation: "valid"', + inProgress: 'External validation: "inProgress"', + pristine: 'External validation: "pristine"', + }, + }, +}; + +export const validationControlNumber = { + options: [ + 'noValidator', + 'syncValidator', + 'asyncValidator', + 'error', + 'valid', + 'inProgress', + 'pristine', + ], + mapping: { + noValidator: undefined, + syncValidator: (value?: number) => { + console.log('Value captured:', value); + if (value && value > 3) { + return 'Too big'; + } else { + return ''; + } + }, + asyncValidator: async (value?: number) => { + console.log('Value captured:', value); + await timeout(1000); + if (value && value > 3) { + return `Last captured: ${value}`; + } else { + return ''; + } + }, + error: ValidationState.error, + valid: ValidationState.valid, + inProgress: ValidationState.inProgress, + pristine: ValidationState.pristine, + }, + control: { + type: 'radio', + labels: { + noValidator: 'No custom validator', + syncValidator: 'Sync validator (value < 4)', + asyncValidator: 'Async validator (value < 4)', + error: 'External validation: "error"', + valid: 'External validation: "valid"', + inProgress: 'External validation: "inProgress"', + pristine: 'External validation: "pristine"', + }, + }, +}; diff --git a/src/internal/inputs/useExternalValidation.ts b/src/internal/inputs/useExternalValidation.ts index a4393a4f..962377ef 100644 --- a/src/internal/inputs/useExternalValidation.ts +++ b/src/internal/inputs/useExternalValidation.ts @@ -1,11 +1,12 @@ import type {Dispatch, SetStateAction, MutableRefObject} from 'react'; -import {useEffect, useState, useCallback} from 'react'; +import {useEffect} from 'react'; +import type {ValidationProps} from '@/internal/inputs'; import {ValidationState} from '@/internal/inputs'; export type Props = { - validationState?: keyof typeof ValidationState; - setValidity: Dispatch>; + validation?: ValidationProps['validation']; + setValidity: Dispatch>; inputRef: MutableRefObject; errorMessage?: string; }; @@ -13,36 +14,29 @@ export type Props = { /** * React hook designed to contain effects which synchronize input validation * with external validation state via prop or context (TODO). - * NB! On change validation takes preference. * @see ValidationState */ -export const useExternalValidation = ({ - validationState: validationStateProp, - inputRef, - setValidity, - errorMessage = ValidationState.error, -}: Props) => { - const [validationState, setValidationState] = useState(validationStateProp); - - useEffect(() => { - setValidationState(validationStateProp); - }, [validationStateProp]); - +export const useExternalValidation = ({validation, inputRef, setValidity, errorMessage}: Props) => { useEffect(() => { - // Empty string is considered to be positive validation result for HTMLInputElement.setCustomValidity - const normalizedErrorMessage = errorMessage ? errorMessage : ValidationState.error; - if (validationState === ValidationState.error && inputRef.current) { - inputRef.current.setCustomValidity(normalizedErrorMessage); - setValidity(ValidationState.error); - } else if (validationState && inputRef.current) { - inputRef.current.setCustomValidity(''); - setValidity(validationState); + if (typeof validation === 'string') { + setValidity(validation); + switch (validation) { + case 'valid': { + inputRef.current?.setCustomValidity(''); + break; + } + case 'error': { + inputRef.current?.setCustomValidity(errorMessage || ValidationState.error); + break; + } + case 'inProgress': { + inputRef.current?.setCustomValidity(errorMessage || ValidationState.inProgress); + break; + } + default: { + inputRef.current?.setCustomValidity(''); + } + } } - }, [errorMessage, inputRef, setValidity, validationState]); - - const resetExternalValidation = useCallback(() => { - setValidationState(undefined); - }, []); - - return {resetExternalValidation}; + }, [errorMessage, inputRef, setValidity, validation]); }; diff --git a/src/internal/inputs/useHandleFormReset.ts b/src/internal/inputs/useHandleFormReset.ts index f51460f2..2b0b28ab 100644 --- a/src/internal/inputs/useHandleFormReset.ts +++ b/src/internal/inputs/useHandleFormReset.ts @@ -3,11 +3,11 @@ import {useEffect} from 'react'; import {useFormSelectors} from '@/lib/Form'; -import type {ValidationState} from './ValidationProps.ts'; +import {ValidationState} from './ValidationProps.ts'; -export const useHandleFormReset = (setValidity: Dispatch) => { +export const useHandleFormReset = (setValidity: Dispatch) => { const {pristine} = useFormSelectors(); useEffect(() => { - pristine && setValidity(null); + pristine && setValidity(ValidationState.pristine); }, [pristine, setValidity]); }; diff --git a/src/internal/inputs/useValidation.ts b/src/internal/inputs/useValidation.ts index 661870b8..312bc9e8 100644 --- a/src/internal/inputs/useValidation.ts +++ b/src/internal/inputs/useValidation.ts @@ -1,8 +1,8 @@ -import type {FormEvent} from 'react'; +import type {FormEvent, Dispatch, SetStateAction} from 'react'; import {useCallback, useState} from 'react'; import AwesomeDebouncePromise from 'awesome-debounce-promise'; -import type {ValidationProps} from './ValidationProps.ts'; +import type {ValidationProps, ValidatorFn} from './ValidationProps.ts'; import {ValidationState} from './ValidationProps.ts'; import {defaultValidator} from './defaultValidator.ts'; import {useHandleFormReset} from './useHandleFormReset.ts'; @@ -19,31 +19,15 @@ const getValue = ( : (event.target as TElement).value; }; -export const useValidation = ({ +const useValidatorFn = ({ validatorFn, -}: ValidationProps) => { - const hasCustomValidation = validatorFn !== defaultValidator; - const [customValidation, setCustomValidation] = useState(hasCustomValidation); - - const isAsync = validatorFn?.constructor.name === 'AsyncFunction'; - - const [validity, setValidity] = useState(null); - - useHandleFormReset(setValidity); - - const reportValidity = useCallback( - (event: TEvent) => { - const isValid = (event.target as TElement).reportValidity(); - if (!isValid && !customValidation) { - setCustomValidation(true); - } - const validState = customValidation ? ValidationState.valid : null; - const nextValidationState = isValid ? validState : ValidationState.error; - setValidity(nextValidationState); - }, - [customValidation, setValidity] - ); - + reportValidity, + setValidity, +}: { + validatorFn: ValidatorFn; + reportValidity: (event: TEvent) => void; + setValidity: Dispatch>; +}) => { const createValidatorSync = useCallback( (mode: InputMode, event: TEvent) => { const value = getValue(event, mode); @@ -59,6 +43,8 @@ export const useValidation = {}, []); + // eslint-disable-next-line react-hooks/exhaustive-deps const debouncedValidator = useCallback(AwesomeDebouncePromise(validatorFn!, 1000), [ validatorFn, @@ -86,22 +72,89 @@ export const useValidation = { + if (typeof validation === 'function') { + return validation; + } + return defaultValidator; +}; + +enum Modes { + async = 'async', + external = 'external', + sync = 'sync', +} + +const getMode = (validation: ValidationProps['validation']) => { + if (validation?.constructor.name === 'AsyncFunction') { + return Modes.async; + } else if (typeof validation === 'string') { + return Modes.external; + } + + return Modes.sync; +}; + +export const useValidation = ({ + validation, +}: ValidationProps) => { + const validatorFn = createValidatorFn(validation); + const mode = getMode(validation); + + const [validity, setValidity] = useState( + ValidationState.pristine + ); + + useHandleFormReset(setValidity); + + // reportValidity gets invoked only if validation is set to custom validator fn + const reportValidity = useCallback((event: TEvent) => { + const isValid = (event.target as TElement).reportValidity(); + const nextValidationState = isValid ? ValidationState.valid : ValidationState.error; + setValidity(nextValidationState); + }, []); + + const {createValidatorAsync, createValidatorSync, createValidatorExternal} = useValidatorFn({ + validatorFn, + reportValidity, + setValidity, + }); + const validateInteractive = useCallback( (event: TEvent) => { - return isAsync - ? createValidatorAsync('interactive', event) - : createValidatorSync('interactive', event); + switch (mode) { + case 'sync': { + return createValidatorSync('interactive', event); + } + case 'async': { + return createValidatorAsync('interactive', event); + } + case 'external': { + return createValidatorExternal(); + } + } }, - [createValidatorAsync, createValidatorSync, isAsync] + [createValidatorAsync, createValidatorExternal, createValidatorSync, mode] ); const validateTextual = useCallback( (event: TEvent) => { - return isAsync - ? createValidatorAsync('textual', event) - : createValidatorSync('textual', event); + switch (mode) { + case 'sync': { + return createValidatorSync('textual', event); + } + case 'async': { + return createValidatorAsync('textual', event); + } + case 'external': { + return createValidatorExternal(); + } + } }, - [createValidatorSync, isAsync, createValidatorAsync] + [mode, createValidatorSync, createValidatorAsync, createValidatorExternal] ); return {validateInteractive, validateTextual, validity, setValidity}; diff --git a/src/internal/inputs/useValidationIcon.tsx b/src/internal/inputs/useValidationIcon.tsx new file mode 100644 index 00000000..33f7a5d5 --- /dev/null +++ b/src/internal/inputs/useValidationIcon.tsx @@ -0,0 +1,14 @@ +import {Fragment} from 'react'; + +import {IconError, IconLoader, IconValid} from '@/internal/Icons'; + +import {ValidationState} from './ValidationProps.ts'; + +export const useValidationIcon = (validity: keyof typeof ValidationState) => { + return { + [ValidationState.error]: IconError, + [ValidationState.valid]: IconValid, + [ValidationState.inProgress]: IconLoader, + [ValidationState.pristine]: () => , + }[validity!]; +}; diff --git a/src/lib/Form/Form.stories.tsx b/src/lib/Form/Form.stories.tsx index b04a2687..2c05cc65 100644 --- a/src/lib/Form/Form.stories.tsx +++ b/src/lib/Form/Form.stories.tsx @@ -107,18 +107,18 @@ export const Primary: Story = { /> - + - + @@ -181,7 +181,7 @@ export const ComplexValidation: Story = { }, }, render: args => { - const validatorFn = ( + const validation = ( value: unknown, _: unknown, formState: Record @@ -204,7 +204,7 @@ export const ComplexValidation: Story = { @@ -213,3 +213,206 @@ export const ComplexValidation: Story = { ); }, }; + +// const onSubmit = (data: unknown) => console.log(data); +// +// type Inputs = { +// exampleHint: string; +// }; +// +// export const ExternalValidation1: Story = { +// name: 'Hook form validation', +// args: { +// onSubmit: (event, state) => { +// event.preventDefault(); +// console.log('onSubmit', state); +// }, +// }, +// render: () => { +// /** +// * React hook form basic setup. Click Submit to validate. +// * @see https://react-hook-form.com/docs/useform +// */ +// const { +// register, +// handleSubmit, +// formState: {errors}, +// } = useForm(); +// +// const validityState = errors.exampleHint ? ValidationState.error : ValidationState.valid; +// +// return ( +//
+// +// +// +//
+// +//
+//
+// ); +// }, +// }; +// +// /** +// * Form validation schema +// * @see https://github.com/jquense/yup?tab=readme-ov-file#schema-basics +// */ +// const schema = yup +// .object({ +// firstName: yup.string().required(), +// age: yup.number().nullable().positive().integer(), +// }) +// .required(); +// +// export const ExternalValidation2: Story = { +// name: 'Hook form validation state', +// args: { +// onSubmit: (event, state) => { +// event.preventDefault(); +// console.log('onSubmit', state); +// }, +// }, +// render: () => { +// /** +// * React hook form basic setup +// * @see https://react-hook-form.com/docs/useform +// */ +// const {register, handleSubmit, formState} = useForm({ +// // Here set schema validation +// resolver: yupResolver(schema), +// defaultValues: { +// firstName: undefined, +// age: null, +// }, +// }); +// +// const getValidity = useCallback( +// (fieldName: string) => { +// // @ts-ignore +// const validState = formState.dirtyFields[fieldName] +// ? ValidationState.valid +// : ValidationState.pristine; +// // @ts-ignore +// return formState.errors[fieldName] ? ValidationState.error : validState; +// }, +// [formState.dirtyFields, formState.errors] +// ); +// +// return ( +//
+// +// +// +// +// +// +//
+// +// +//
+//
+// ); +// }, +// }; +// +// const validationSchemaFormik = yup.object({ +// email: yup.string().email('Enter a valid email').required('Email is required'), +// password: yup +// .string() +// .min(8, 'Password should be of minimum 8 characters length') +// .required('Password is required'), +// }); +// +// export const ExternalValidation3: Story = { +// name: 'Hook form validation Formik', +// args: { +// onSubmit: (event, state) => { +// event.preventDefault(); +// console.log('onSubmit', state); +// }, +// }, +// render: () => { +// /** +// * React hook form basic setup +// * @see https://react-hook-form.com/docs/useform +// */ +// const formik = useFormik({ +// initialValues: { +// email: 'foobar@example.com', +// password: 'foobar', +// }, +// validationSchema: validationSchemaFormik, +// onSubmit: values => { +// console.log(values); +// }, +// }); +// const emailValidationState = +// formik.touched.email && Boolean(formik.errors.email) +// ? ValidationState.error +// : ValidationState.valid; +// const emailHint = formik.touched.email ? formik.errors.email : undefined; +// const passwordValidationState = +// formik.touched.password && Boolean(formik.errors.password) +// ? ValidationState.error +// : ValidationState.valid; +// const passwordHint = formik.touched.password ? formik.errors.password : undefined; +// +// return ( +//
+// {/* This input field displays hint below input when validation fails */} +// +// +// +// {/* This input field tries to display browser input error tooltip */} +// +// +// +//
+// +//
+//
+// ); +// }, +// }; diff --git a/src/lib/InputCheckbox/InputCheckbox.stories.tsx b/src/lib/InputCheckbox/InputCheckbox.stories.tsx index fba046f7..5fa298bf 100644 --- a/src/lib/InputCheckbox/InputCheckbox.stories.tsx +++ b/src/lib/InputCheckbox/InputCheckbox.stories.tsx @@ -3,7 +3,7 @@ import {useState, useCallback} from 'react'; import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; -import {validatorAsyncBoolean, validatorSyncBoolean} from '@/internal/inputs'; +import {validationControlBoolean} from '@/internal/inputs/storybook/validationControl.ts'; import {InputCheckbox} from './InputCheckbox.tsx'; @@ -23,6 +23,7 @@ const meta = { value: '', disabled: false, required: false, + errorMessage: 'External validation error', }, argTypes: { value: {control: 'text'}, @@ -96,23 +97,7 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values - mapping: { - noValidator: undefined, - syncValidator: validatorSyncBoolean, - asyncValidator: validatorAsyncBoolean, - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noValidator: 'No custom validator', - syncValidator: 'Sync validator (value !== true)', - asyncValidator: 'Async validator (value !== true)', - }, - }, - }, + validation: validationControlBoolean, }, } as Meta; @@ -168,23 +153,6 @@ Controlled.argTypes = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values - mapping: { - noValidator: undefined, - syncValidator: validatorSyncBoolean, - asyncValidator: validatorAsyncBoolean, - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noValidator: 'No custom validator', - syncValidator: 'Sync validator (value !== true)', - asyncValidator: 'Async validator (value !== true)', - }, - }, - }, }; Controlled.parameters = { diff --git a/src/lib/InputCheckbox/InputCheckbox.tsx b/src/lib/InputCheckbox/InputCheckbox.tsx index e6033c46..05ff3e5a 100644 --- a/src/lib/InputCheckbox/InputCheckbox.tsx +++ b/src/lib/InputCheckbox/InputCheckbox.tsx @@ -5,17 +5,16 @@ import classNames from 'classnames'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import { ValidationState, - defaultValidator, useValidation, useRevalidateOnFormChange, useExternalValidation, + useValidationIcon, } from '@/internal/inputs'; import type { NativePropsInteractive, CallbackPropsInteractive, ValidationProps, } from '@/internal/inputs'; -import {IconError, IconLoader, IconValid} from '@/internal/Icons'; import {useInternalId} from '@/internal/hooks/useInternalId.ts'; import {useInternalRef} from '@/internal/hooks/useInternalRef.ts'; @@ -45,28 +44,23 @@ export const InputCheckbox = forwardRef( defaultChecked, id: idProp, label, - validatorFn = defaultValidator, required, revalidateOnFormChange, - validationState, + validation, errorMessage, ...nativeProps }, ref ) => { const id = useInternalId(idProp); - const {validateInteractive, validity, setValidity} = useValidation({validatorFn}); + const {validateInteractive, validity, setValidity} = useValidation({validation}); const inputRef = useInternalRef(ref); useRevalidateOnFormChange(inputRef, validateInteractive, revalidateOnFormChange); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const handleChange = useCallback( (event: ChangeEvent) => { diff --git a/src/lib/InputColor/InputColor.stories.tsx b/src/lib/InputColor/InputColor.stories.tsx index 6d867266..f01ef384 100644 --- a/src/lib/InputColor/InputColor.stories.tsx +++ b/src/lib/InputColor/InputColor.stories.tsx @@ -2,6 +2,8 @@ import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; import {type ChangeEvent, useCallback, useState} from 'react'; +import {ValidationState} from '@/internal/inputs'; + import {InputColor} from './InputColor.tsx'; const meta = { @@ -20,6 +22,7 @@ const meta = { required: false, placeholder: '#000000', disabled: false, + errorMessage: 'External validation error', }, argTypes: { value: { @@ -107,6 +110,25 @@ const meta = { }, }, }, + validation: { + // TODO: add function validation example + options: ['error', 'valid', 'inProgress', 'pristine'], + mapping: { + error: ValidationState.error, + valid: ValidationState.valid, + inProgress: ValidationState.inProgress, + pristine: ValidationState.pristine, + }, + control: { + type: 'radio', + labels: { + error: 'External validation: "error"', + valid: 'External validation: "valid"', + inProgress: 'External validation: "inProgress"', + pristine: 'External validation: "pristine"', + }, + }, + }, }, } as Meta; diff --git a/src/lib/InputColor/InputColor.tsx b/src/lib/InputColor/InputColor.tsx index 7a82f619..4baaf6ab 100644 --- a/src/lib/InputColor/InputColor.tsx +++ b/src/lib/InputColor/InputColor.tsx @@ -4,7 +4,7 @@ import {forwardRef, useCallback} from 'react'; import classNames from 'classnames'; import {useLocalTheme} from 'css-vars-hook'; -import {IconError, IconLoader, IconPalette, IconValid} from '@/internal/Icons'; +import {IconPalette} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type { CallbackPropsTextual, @@ -12,12 +12,7 @@ import type { NativePropsInteractive, } from '@/internal/inputs'; import {useRevalidateOnFormChange} from '@/internal/inputs'; -import { - ValidationState, - useExternalValidation, - useValidation, - defaultValidator, -} from '@/internal/inputs'; +import {useExternalValidation, useValidation, useValidationIcon} from '@/internal/inputs'; import {useInternalId} from '@/internal/hooks/useInternalId.ts'; import {useInternalRef} from '@/internal/hooks/useInternalRef.ts'; @@ -56,29 +51,24 @@ export const InputColor = forwardRef( defaultValue, id: idProp, predefinedColors = [], - validationState, errorMessage, revalidateOnFormChange, - validatorFn = defaultValidator, + validation, ...nativeProps }, ref ) => { const {validity, setValidity, validateTextual} = useValidation({ - validatorFn, + validation, }); const id = useInternalId(idProp); const inputRef = useInternalRef(ref); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const {LocalRoot, setTheme} = useLocalTheme(); const displayValue = (value ?? defaultValue) as string; diff --git a/src/lib/InputDate/InputDate.stories.tsx b/src/lib/InputDate/InputDate.stories.tsx index 1ce4b659..50f6dc2e 100644 --- a/src/lib/InputDate/InputDate.stories.tsx +++ b/src/lib/InputDate/InputDate.stories.tsx @@ -4,6 +4,7 @@ import type {ChangeEvent} from 'react'; import {useState, useCallback} from 'react'; import {timeout} from '@/internal/inputs/validatorMocks.ts'; +import {ValidationState} from '@/internal/inputs'; import {InputDate} from './InputDate.tsx'; @@ -24,6 +25,7 @@ const meta = { placeholder: 'YYYY-MM-DD', disabled: false, readOnly: false, + errorMessage: 'External validation error', }, argTypes: { value: { @@ -97,8 +99,16 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values + validation: { + options: [ + 'noValidator', + 'syncValidator', + 'asyncValidator', + 'error', + 'valid', + 'inProgress', + 'pristine', + ], mapping: { noValidator: undefined, syncValidator: (value?: unknown) => { @@ -118,14 +128,21 @@ const meta = { return `Last captured: ${value}`; } }, - }, // Maps serializable option values to complex arg values + error: ValidationState.error, + valid: ValidationState.valid, + inProgress: ValidationState.inProgress, + pristine: ValidationState.pristine, + }, control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined + type: 'radio', labels: { - // 'labels' maps option values to string labels noValidator: 'No custom validator', syncValidator: 'Sync validator (2018-07-23)', asyncValidator: 'Async validator (2018-07-23)', + error: 'External validation: "error"', + valid: 'External validation: "valid"', + inProgress: 'External validation: "inProgress"', + pristine: 'External validation: "pristine"', }, }, }, diff --git a/src/lib/InputDate/InputDate.tsx b/src/lib/InputDate/InputDate.tsx index 903d6f4e..3ce2a179 100644 --- a/src/lib/InputDate/InputDate.tsx +++ b/src/lib/InputDate/InputDate.tsx @@ -3,13 +3,13 @@ import {useRef} from 'react'; import {forwardRef, useCallback} from 'react'; import classNames from 'classnames'; -import {IconError, IconValid, IconLoader, IconCalendar} from '@/internal/Icons'; +import {IconCalendar} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsTextual, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; import { useRevalidateOnFormChange, useExternalValidation, - defaultValidator, + useValidationIcon, ValidationState, useValidation, } from '@/internal/inputs'; @@ -38,9 +38,8 @@ export const InputDate = forwardRef( onKeyDown = () => {}, onKeyUp = () => {}, defaultValue, - validatorFn = defaultValidator, revalidateOnFormChange, - validationState, + validation, errorMessage, ...nativeProps }, @@ -49,18 +48,14 @@ export const InputDate = forwardRef( const id = useInternalId(idProp); const labelRef = useRef(null); - const {validateTextual, validity, setValidity} = useValidation({validatorFn}); + const {validateTextual, validity, setValidity} = useValidation({validation}); const inputRef = useInternalRef(ref); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const displayValue = (value ?? defaultValue) as string; const handleChange = useCallback( diff --git a/src/lib/InputFile/InputFile.stories.tsx b/src/lib/InputFile/InputFile.stories.tsx index f2f9fe38..c60f5232 100644 --- a/src/lib/InputFile/InputFile.stories.tsx +++ b/src/lib/InputFile/InputFile.stories.tsx @@ -1,6 +1,8 @@ import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; +import {ValidationState} from '@/internal/inputs'; + import {InputFile} from './InputFile.tsx'; const meta = { @@ -98,6 +100,25 @@ const meta = { disable: true, }, }, + validation: { + // TODO: add function validation example + options: ['error', 'valid', 'inProgress', 'pristine'], + mapping: { + error: ValidationState.error, + valid: ValidationState.valid, + inProgress: ValidationState.inProgress, + pristine: ValidationState.pristine, + }, + control: { + type: 'radio', + labels: { + error: 'External validation: "error"', + valid: 'External validation: "valid"', + inProgress: 'External validation: "inProgress"', + pristine: 'External validation: "pristine"', + }, + }, + }, }, } as Meta; diff --git a/src/lib/InputFile/InputFile.tsx b/src/lib/InputFile/InputFile.tsx index 1addff3b..229150e2 100644 --- a/src/lib/InputFile/InputFile.tsx +++ b/src/lib/InputFile/InputFile.tsx @@ -5,13 +5,13 @@ import {forwardRef, useCallback} from 'react'; import classNames from 'classnames'; import {useLocalTheme} from 'css-vars-hook'; -import {IconError, IconValid, IconLoader, IconFile} from '@/internal/Icons'; +import {IconFile} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsTextual, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; import { useRevalidateOnFormChange, useExternalValidation, - defaultValidator, + useValidationIcon, ValidationState, useValidation, } from '@/internal/inputs'; @@ -44,10 +44,9 @@ export const InputFile = forwardRef( onKeyUp = () => {}, defaultValue, size = 16, - validationState, errorMessage, revalidateOnFormChange, - validatorFn = defaultValidator, + validation, ...nativeProps }, ref @@ -63,17 +62,13 @@ export const InputFile = forwardRef( const id = useInternalId(idProp); const [filename, setFileName] = useState(''); const {validateTextual, validity, setValidity} = useValidation({ - validatorFn, + validation, }); const inputRef = useInternalRef(ref); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const handleChange = useCallback( (event: ChangeEvent) => { onChange(event); diff --git a/src/lib/InputGroup/InputGroup.stories.tsx b/src/lib/InputGroup/InputGroup.stories.tsx index bda57da0..b5fb7694 100644 --- a/src/lib/InputGroup/InputGroup.stories.tsx +++ b/src/lib/InputGroup/InputGroup.stories.tsx @@ -215,7 +215,7 @@ export const CheckboxCustomValidation: Story = { label="This is a foo name" key="zork" required - validatorFn={value => { + validation={value => { return !value ? 'Custom message' : ''; }} />, @@ -230,7 +230,7 @@ export const CheckboxCustomValidation: Story = { value="bazz" label="This is a bazz name" key="bork" - validatorFn={value => { + validation={value => { return !value ? 'Custom message' : ''; }} />, diff --git a/src/lib/InputNumber/InputNumber.stories.tsx b/src/lib/InputNumber/InputNumber.stories.tsx index c3276ad4..1c760396 100644 --- a/src/lib/InputNumber/InputNumber.stories.tsx +++ b/src/lib/InputNumber/InputNumber.stories.tsx @@ -2,7 +2,7 @@ import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; import {type ChangeEvent, useCallback, useState} from 'react'; -import {timeout} from '@/internal/inputs'; +import {validationControlNumber} from '@/internal/inputs/storybook/validationControl.ts'; import {InputNumber} from './InputNumber.tsx'; @@ -103,36 +103,7 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values - mapping: { - noValidator: undefined, - syncValidator: (value: number) => { - if (value > 66) { - return 'Too big'; - } - return ''; - }, - asyncValidator: async (value: number) => { - console.log('Value captured:', value); - await timeout(1000); - if (value > 66) { - return `Too big. Value captured: ${value}`; - } else { - return ''; - } - }, - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noValidator: 'No custom validator', - syncValidator: 'Sync validator (value > 66 )', - asyncValidator: 'Async validator (value > 66 )', - }, - }, - }, + validation: validationControlNumber, }, } as Meta; diff --git a/src/lib/InputNumber/InputNumber.tsx b/src/lib/InputNumber/InputNumber.tsx index 8a657db7..2e9660e4 100644 --- a/src/lib/InputNumber/InputNumber.tsx +++ b/src/lib/InputNumber/InputNumber.tsx @@ -1,18 +1,17 @@ import type {ChangeEvent, InputHTMLAttributes, FormEvent} from 'react'; -import {useMemo} from 'react'; -import {forwardRef, useCallback} from 'react'; +import {forwardRef, useCallback, useMemo} from 'react'; import classNames from 'classnames'; import {useLocalTheme} from 'css-vars-hook'; -import {IconError, IconValid, IconLoader, IconArrowUp, IconArrowDown} from '@/internal/Icons'; +import {IconArrowUp, IconArrowDown} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsNumeric, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; import { ValidationState, - defaultValidator, useValidation, useExternalValidation, useRevalidateOnFormChange, + useValidationIcon, } from '@/internal/inputs'; import {useInternalRef} from '@/internal/hooks/useInternalRef.ts'; @@ -44,27 +43,22 @@ export const InputNumber = forwardRef( onKeyDown = () => {}, onKeyUp = () => {}, defaultValue, - validatorFn = defaultValidator, size = 10, step = 1, revalidateOnFormChange, - validationState, - errorMessage, + errorMessage = ValidationState.error, + validation, ...nativeProps }, ref ) => { - const {validateTextual, validity, setValidity} = useValidation({validatorFn}); + const {validateTextual, validity, setValidity} = useValidation({validation}); const inputRef = useInternalRef(ref); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const handleChange = useCallback( (event: ChangeEvent) => { onChange(event); diff --git a/src/lib/InputPassword/InputPassword.stories.tsx b/src/lib/InputPassword/InputPassword.stories.tsx index 37c960d9..89fa2da6 100644 --- a/src/lib/InputPassword/InputPassword.stories.tsx +++ b/src/lib/InputPassword/InputPassword.stories.tsx @@ -2,7 +2,8 @@ import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; import {type ChangeEvent, useCallback, useState} from 'react'; -import {validatorAsync, validatorSync} from '@/internal/inputs'; +import {validationControl} from '@/internal/inputs/storybook/validationControl.ts'; +import {patternControl} from '@/internal/inputs/storybook/patternControl.ts'; import {InputPassword} from './InputPassword.tsx'; @@ -107,43 +108,13 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values - mapping: { - noValidator: undefined, - syncValidator: validatorSync, - asyncValidator: validatorAsync, - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noValidator: 'No custom validator', - syncValidator: 'Sync validator (value.length < 4)', - asyncValidator: 'Async validator (value.length < 4)', - }, - }, - }, + validation: validationControl, prefix: { table: { disable: true, }, }, - pattern: { - options: ['noPattern', 'withPattern'], // An array of serializable values - mapping: { - noPattern: undefined, - withPattern: '[^@\\s]+@[^@\\s]+', - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noPattern: 'No pattern', - withPattern: 'With pattern ([^@\\s]+@[^@\\s]+)', - }, - }, - }, + pattern: patternControl, }, } as Meta; diff --git a/src/lib/InputPassword/InputPassword.tsx b/src/lib/InputPassword/InputPassword.tsx index cc0de10c..66fa56a8 100644 --- a/src/lib/InputPassword/InputPassword.tsx +++ b/src/lib/InputPassword/InputPassword.tsx @@ -4,12 +4,12 @@ import {useState} from 'react'; import {forwardRef, useCallback} from 'react'; import classNames from 'classnames'; -import {IconError, IconValid, IconLoader, IconLock, IconLockOpen} from '@/internal/Icons'; +import {IconLock, IconLockOpen} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsTextual, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; import { ValidationState, - defaultValidator, + useValidationIcon, useValidation, useRevalidateOnFormChange, useExternalValidation, @@ -43,28 +43,23 @@ export const InputPassword = forwardRef( onKeyDown = () => {}, onKeyUp = () => {}, defaultValue, - validatorFn = defaultValidator, id, readOnly, size = 16, revalidateOnFormChange, - validationState, + validation, errorMessage, ...nativeProps }, ref ) => { - const {validateTextual, validity, setValidity} = useValidation({validatorFn}); + const {validateTextual, validity, setValidity} = useValidation({validation}); const inputRef = useInternalRef(ref); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const handleChange = useCallback( (event: ChangeEvent) => { onChange(event); diff --git a/src/lib/InputRange/InputRange.stories.tsx b/src/lib/InputRange/InputRange.stories.tsx index 16b24375..9682df74 100644 --- a/src/lib/InputRange/InputRange.stories.tsx +++ b/src/lib/InputRange/InputRange.stories.tsx @@ -2,8 +2,8 @@ import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; import {type ChangeEvent, useCallback, useState} from 'react'; -import {CloudUpload} from '@/internal/Icons'; -import {timeout} from '@/internal/inputs'; +import {prefixControl} from '@/internal/inputs/storybook/prefixControl.ts'; +import {validationControlNumber} from '@/internal/inputs/storybook/validationControl.ts'; import {InputRange} from './InputRange.tsx'; @@ -91,51 +91,8 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values - mapping: { - noValidator: undefined, - syncValidator: (value: number) => { - if (value > 66) { - return 'Too big'; - } - return ''; - }, - asyncValidator: async (value: number) => { - console.log('Value captured:', value); - await timeout(1000); - if (value > 66) { - return `Too big. Value captured: ${value}`; - } else { - return ''; - } - }, - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noValidator: 'No custom validator', - syncValidator: 'Sync validator (value > 66 )', - asyncValidator: 'Async validator (value > 66 )', - }, - }, - }, - prefix: { - options: ['noPrefix', 'withPrefix'], - mapping: { - noPrefix: undefined, - withPrefix: CloudUpload, - }, - control: { - type: 'radio', - labels: { - // 'labels' maps option values to string labels - noPrefix: 'No prefix', - withPrefix: 'With prefix', - }, - }, - }, + validation: validationControlNumber, + prefix: prefixControl, }, } as Meta; diff --git a/src/lib/InputRange/InputRange.tsx b/src/lib/InputRange/InputRange.tsx index 88697462..db3b959c 100644 --- a/src/lib/InputRange/InputRange.tsx +++ b/src/lib/InputRange/InputRange.tsx @@ -4,12 +4,11 @@ import {forwardRef, useCallback} from 'react'; import classNames from 'classnames'; import {useLocalTheme} from 'css-vars-hook'; -import {IconError, IconValid, IconLoader} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsNumeric, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; import {useExternalValidation} from '@/internal/inputs'; import {useRevalidateOnFormChange} from '@/internal/inputs'; -import {ValidationState, defaultValidator, useValidation} from '@/internal/inputs'; +import {ValidationState, useValidationIcon, useValidation} from '@/internal/inputs'; import {useControllableState} from '@/internal/hooks/useControllableState.ts'; import {useInternalId} from '@/internal/hooks/useInternalId.ts'; import {useInternalRef} from '@/internal/hooks/useInternalRef.ts'; @@ -66,29 +65,24 @@ export const InputRange = forwardRef( onKeyDown = () => {}, onKeyUp = () => {}, defaultValue, - validatorFn = defaultValidator, min = 0, max = 100, bars = 5, scaleUnit = '', revalidateOnFormChange, - validationState, + validation, errorMessage, ...nativeProps }, ref ) => { - const {validateTextual, validity, setValidity} = useValidation({validatorFn}); + const {validateTextual, validity, setValidity} = useValidation({validation}); const inputRef = useInternalRef(ref); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const [displayValue, setDisplayValue] = useControllableState({ value, diff --git a/src/lib/InputText/InputText.stories.tsx b/src/lib/InputText/InputText.stories.tsx index 1a8af2d8..6c9edb8b 100644 --- a/src/lib/InputText/InputText.stories.tsx +++ b/src/lib/InputText/InputText.stories.tsx @@ -2,8 +2,9 @@ import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; import {type ChangeEvent, useCallback, useState} from 'react'; -import {validatorSync, validatorAsync} from '@/internal/inputs'; -import {CloudUpload} from '@/internal/Icons'; +import {prefixControl} from '@/internal/inputs/storybook/prefixControl.ts'; +import {validationControl} from '@/internal/inputs/storybook/validationControl.ts'; +import {patternControl} from '@/internal/inputs/storybook/patternControl.ts'; import {InputText} from './InputText'; @@ -26,6 +27,7 @@ const meta = { disabled: false, autoComplete: 'off', size: 16, + errorMessage: 'External error message example.', }, argTypes: { onClick: { @@ -93,53 +95,9 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values - mapping: { - noValidator: undefined, - syncValidator: validatorSync, - asyncValidator: validatorAsync, - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noValidator: 'No custom validator', - syncValidator: 'Sync validator (value.length < 4)', - asyncValidator: 'Async validator (value.length < 4)', - }, - }, - }, - pattern: { - options: ['noPattern', 'withPattern'], // An array of serializable values - mapping: { - noPattern: undefined, - withPattern: '[^@\\s]+@[^@\\s]+', - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noPattern: 'No pattern', - withPattern: 'With pattern ([^@\\s]+@[^@\\s]+)', - }, - }, - }, - prefix: { - options: ['noPrefix', 'withPrefix'], - mapping: { - noPrefix: undefined, - withPrefix: CloudUpload, - }, - control: { - type: 'radio', - labels: { - // 'labels' maps option values to string labels - noPrefix: 'No prefix', - withPrefix: 'With prefix', - }, - }, - }, + validation: validationControl, + pattern: patternControl, + prefix: prefixControl, }, } as Meta; diff --git a/src/lib/InputText/InputText.tsx b/src/lib/InputText/InputText.tsx index a0f4c40a..a45481d7 100644 --- a/src/lib/InputText/InputText.tsx +++ b/src/lib/InputText/InputText.tsx @@ -1,5 +1,5 @@ -import type {FC, InputHTMLAttributes} from 'react'; -import type {ChangeEvent} from 'react'; +import type {ChangeEvent, FC, InputHTMLAttributes} from 'react'; +import {Fragment} from 'react'; import {forwardRef, useCallback} from 'react'; import classNames from 'classnames'; @@ -8,7 +8,6 @@ import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsTextual, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; import { ValidationState, - defaultValidator, useValidation, useRevalidateOnFormChange, useExternalValidation, @@ -55,42 +54,39 @@ export const InputText = forwardRef( onKeyDown = () => {}, onKeyUp = () => {}, defaultValue, - validatorFn = defaultValidator, readOnly, size = 16, id, required, revalidateOnFormChange, - validationState, - errorMessage, + errorMessage = ValidationState.error, + validation, ...nativeProps }, ref ) => { - const {validateTextual, validity, setValidity} = useValidation({validatorFn}); - const inputRef = useInternalRef(ref); - useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - const {resetExternalValidation} = useExternalValidation({ - inputRef, - setValidity, - validationState, - errorMessage, + const {validateTextual, validity, setValidity} = useValidation({ + validation, }); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); + + useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); + const ValidationIcon = { [ValidationState.error]: IconError, [ValidationState.valid]: IconValid, [ValidationState.inProgress]: IconLoader, + [ValidationState.pristine]: Fragment, }[validity!]; const handleChange = useCallback( (event: ChangeEvent) => { - resetExternalValidation(); onChange(event); }, - [onChange, resetExternalValidation] + [onChange] ); const handleInvalid = useCallback(() => { diff --git a/src/lib/InputTime/InputTime.stories.tsx b/src/lib/InputTime/InputTime.stories.tsx index bfe3fdac..38b72773 100644 --- a/src/lib/InputTime/InputTime.stories.tsx +++ b/src/lib/InputTime/InputTime.stories.tsx @@ -3,7 +3,7 @@ import {fn} from '@storybook/test'; import type {ChangeEvent} from 'react'; import {useCallback, useState} from 'react'; -import {timeout} from '@/internal/inputs'; +import {timeout, ValidationState} from '@/internal/inputs'; import {InputTime} from './InputTime.tsx'; @@ -94,8 +94,16 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values + validation: { + options: [ + 'noValidator', + 'syncValidator', + 'asyncValidator', + 'error', + 'valid', + 'inProgress', + 'pristine', + ], mapping: { noValidator: undefined, syncValidator: (value?: unknown) => { @@ -115,14 +123,21 @@ const meta = { return ''; } }, - }, // Maps serializable option values to complex arg values + error: ValidationState.error, + valid: ValidationState.valid, + inProgress: ValidationState.inProgress, + pristine: ValidationState.pristine, + }, control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined + type: 'radio', labels: { - // 'labels' maps option values to string labels noValidator: 'No custom validator', syncValidator: 'Sync validator (value === 23:23)', asyncValidator: 'Async validator (value === 23:23)', + error: 'External validation: "error"', + valid: 'External validation: "valid"', + inProgress: 'External validation: "inProgress"', + pristine: 'External validation: "pristine"', }, }, }, diff --git a/src/lib/InputTime/InputTime.tsx b/src/lib/InputTime/InputTime.tsx index 97bed25c..ba66a5a2 100644 --- a/src/lib/InputTime/InputTime.tsx +++ b/src/lib/InputTime/InputTime.tsx @@ -2,12 +2,12 @@ import type {ChangeEvent, KeyboardEvent} from 'react'; import {forwardRef, useCallback} from 'react'; import classNames from 'classnames'; -import {IconError, IconValid, IconLoader, IconClock} from '@/internal/Icons'; +import {IconClock} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsTextual, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; import {useExternalValidation} from '@/internal/inputs'; import {useRevalidateOnFormChange} from '@/internal/inputs'; -import {ValidationState, defaultValidator, useValidation} from '@/internal/inputs'; +import {ValidationState, useValidationIcon, useValidation} from '@/internal/inputs'; import {useInternalRef} from '@/internal/hooks/useInternalRef.ts'; import classes from './InputTime.module.css'; @@ -35,25 +35,20 @@ export const InputTime = forwardRef( onKeyDown = () => {}, onKeyUp = () => {}, defaultValue, - validatorFn = defaultValidator, revalidateOnFormChange, - validationState, + validation, errorMessage, ...nativeProps }, ref ) => { const inputRef = useInternalRef(ref); - const {validity, setValidity, validateTextual} = useValidation({validatorFn}); + const {validity, setValidity, validateTextual} = useValidation({validation}); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const handleInvalid = useCallback(() => { setValidity(ValidationState.error); diff --git a/src/lib/Select/Select.stories.tsx b/src/lib/Select/Select.stories.tsx index c6e20789..55667519 100644 --- a/src/lib/Select/Select.stories.tsx +++ b/src/lib/Select/Select.stories.tsx @@ -2,8 +2,8 @@ import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; import {type ChangeEvent, useCallback, useState} from 'react'; -import {validatorAsync, validatorSync} from '@/internal/inputs'; -import {CloudUpload} from '@/internal/Icons'; +import {validationControl} from '@/internal/inputs/storybook/validationControl.ts'; +import {prefixControl} from '@/internal/inputs/storybook/prefixControl.ts'; import {Select} from './Select.tsx'; @@ -91,38 +91,8 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values - mapping: { - noValidator: undefined, - syncValidator: validatorSync, - asyncValidator: validatorAsync, - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noValidator: 'No custom validator', - syncValidator: 'Sync validator (value.length < 4)', - asyncValidator: 'Async validator (value.length < 4)', - }, - }, - }, - prefix: { - options: ['noPrefix', 'withPrefix'], - mapping: { - noPrefix: undefined, - withPrefix: CloudUpload, - }, - control: { - type: 'radio', - labels: { - // 'labels' maps option values to string labels - noPrefix: 'No prefix', - withPrefix: 'With prefix', - }, - }, - }, + validation: validationControl, + prefix: prefixControl, }, } as Meta; diff --git a/src/lib/Select/Select.tsx b/src/lib/Select/Select.tsx index 001cb338..7916ba9f 100644 --- a/src/lib/Select/Select.tsx +++ b/src/lib/Select/Select.tsx @@ -3,7 +3,7 @@ import {forwardRef, useCallback, useState, useMemo} from 'react'; import classNames from 'classnames'; import {useLocalTheme} from 'css-vars-hook'; -import {IconError, IconValid, IconLoader, IconExpand, IconCollapse} from '@/internal/Icons'; +import {IconExpand, IconCollapse} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type { NativePropsInteractive, @@ -12,7 +12,7 @@ import type { } from '@/internal/inputs'; import {useExternalValidation} from '@/internal/inputs'; import {useRevalidateOnFormChange} from '@/internal/inputs'; -import {ValidationState, defaultValidator, useValidation} from '@/internal/inputs'; +import {ValidationState, useValidationIcon, useValidation} from '@/internal/inputs'; import {useInternalId} from '@/internal/hooks/useInternalId.ts'; import {useInternalRef} from '@/internal/hooks/useInternalRef.ts'; @@ -49,13 +49,12 @@ export const Select = forwardRef( onBlur = () => {}, onKeyDown = () => {}, onKeyUp = () => {}, - validatorFn = defaultValidator, id, multiple, children, size = 16, revalidateOnFormChange, - validationState, + validation, errorMessage, ...nativeProps }, @@ -69,17 +68,13 @@ export const Select = forwardRef( [size] ); - const {validateTextual, validity, setValidity} = useValidation({validatorFn}); + const {validateTextual, validity, setValidity} = useValidation({validation}); const inputRef = useInternalRef(ref); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const handleChange = useCallback( (event: ChangeEvent) => { onChange(event); diff --git a/src/lib/Textarea/Textarea.stories.tsx b/src/lib/Textarea/Textarea.stories.tsx index 6d92b9e1..54805831 100644 --- a/src/lib/Textarea/Textarea.stories.tsx +++ b/src/lib/Textarea/Textarea.stories.tsx @@ -2,8 +2,8 @@ import type {Meta, StoryObj} from '@storybook/react'; import {fn} from '@storybook/test'; import {type ChangeEvent, useCallback, useState} from 'react'; -import {validatorAsync, validatorSync} from '@/internal/inputs'; -import {CloudUpload} from '@/internal/Icons'; +import {validationControl} from '@/internal/inputs/storybook/validationControl.ts'; +import {prefixControl} from '@/internal/inputs/storybook/prefixControl.ts'; import {Textarea} from './Textarea.tsx'; @@ -100,38 +100,8 @@ const meta = { disable: true, }, }, - validatorFn: { - options: ['noValidator', 'syncValidator', 'asyncValidator'], // An array of serializable values - mapping: { - noValidator: undefined, - syncValidator: validatorSync, - asyncValidator: validatorAsync, - }, // Maps serializable option values to complex arg values - control: { - type: 'radio', // Type 'select' is automatically inferred when 'options' is defined - labels: { - // 'labels' maps option values to string labels - noValidator: 'No custom validator', - syncValidator: 'Sync validator (value.length < 4)', - asyncValidator: 'Async validator (value.length < 4)', - }, - }, - }, - prefix: { - options: ['noPrefix', 'withPrefix'], - mapping: { - noPrefix: undefined, - withPrefix: CloudUpload, - }, - control: { - type: 'radio', - labels: { - // 'labels' maps option values to string labels - noPrefix: 'No prefix', - withPrefix: 'With prefix', - }, - }, - }, + validation: validationControl, + prefix: prefixControl, }, } as Meta; diff --git a/src/lib/Textarea/Textarea.tsx b/src/lib/Textarea/Textarea.tsx index cd8101c3..c812ff0e 100644 --- a/src/lib/Textarea/Textarea.tsx +++ b/src/lib/Textarea/Textarea.tsx @@ -3,12 +3,11 @@ import {forwardRef, useCallback, useMemo} from 'react'; import classNames from 'classnames'; import {useLocalTheme} from 'css-vars-hook'; -import {IconError, IconValid, IconLoader} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsTextual, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; import {useExternalValidation} from '@/internal/inputs'; import {useRevalidateOnFormChange} from '@/internal/inputs'; -import {ValidationState, defaultValidator, useValidation} from '@/internal/inputs'; +import {ValidationState, useValidationIcon, useValidation} from '@/internal/inputs'; import {useInternalId} from '@/internal/hooks/useInternalId.ts'; import {useInternalRef} from '@/internal/hooks/useInternalRef.ts'; @@ -61,30 +60,25 @@ export const Textarea = forwardRef( onKeyDown = () => {}, onKeyUp = () => {}, defaultValue, - validatorFn = defaultValidator, id, readOnly, cols = 20, rows = 3, resize = 'both', revalidateOnFormChange, - validationState, + validation, errorMessage, ...nativeProps }, ref ) => { - const {validateTextual, validity, setValidity} = useValidation({validatorFn}); + const {validateTextual, validity, setValidity} = useValidation({validation}); const inputRef = useInternalRef(ref); useRevalidateOnFormChange(inputRef, validateTextual, revalidateOnFormChange); - useExternalValidation({inputRef, setValidity, validationState, errorMessage}); + useExternalValidation({errorMessage, inputRef, setValidity, validation}); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const ValidationIcon = useValidationIcon(validity); const handleChange = useCallback( (event: ChangeEvent) => { onChange(event); diff --git a/templates/Input/TemplateName.tsx b/templates/Input/TemplateName.tsx index 8fd478c1..caa55cb5 100644 --- a/templates/Input/TemplateName.tsx +++ b/templates/Input/TemplateName.tsx @@ -2,10 +2,9 @@ import type {ChangeEvent, FC} from 'react'; import {forwardRef, useCallback} from 'react'; import classNames from 'classnames'; -import {IconError, IconValid, IconLoader} from '@/internal/Icons'; import type {DataAttributes, LibraryProps} from '@/internal/LibraryAPI'; import type {NativePropsTextual, CallbackPropsTextual, ValidationProps} from '@/internal/inputs'; -import {ValidationState, defaultValidator, useValidation} from '@/internal/inputs'; +import {useValidationIcon, ValidationState, useValidation} from '@/internal/inputs'; import classes from './TemplateName.module.css'; @@ -31,17 +30,14 @@ export const TemplateName = forwardRef( onKeyDown = () => {}, onKeyUp = () => {}, defaultValue, - validatorFn = defaultValidator, + validation, + errorMessage, ...nativeProps }, ref ) => { - const {validateTextual, validity, setValidity} = useValidation({validatorFn}); - const ValidationIcon = { - [ValidationState.error]: IconError, - [ValidationState.valid]: IconValid, - [ValidationState.inProgress]: IconLoader, - }[validity!]; + const {validateTextual, validity, setValidity} = useValidation({validation}); + const ValidationIcon = useValidationIcon(validity); const handleChange = useCallback( (event: ChangeEvent) => { onChange(event);