diff --git a/.eslintrc.json b/.eslintrc.json index 965d8d893..e172fedbc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,6 +5,7 @@ "next/core-web-vitals", "plugin:storybook/recommended" ], + "plugins": ["@eslint-react/eslint-plugin"], "rules": { "react/no-unescaped-entities": "off", "@typescript-eslint/no-unused-vars": [ @@ -18,6 +19,10 @@ "sonarjs/prefer-immediate-return": "warn" }, "overrides": [ + { + "files": ["**/*.{ts,tsx}"], + "extends": ["plugin:@eslint-react/recommended-legacy"] + }, { "files": ["./src/**/*.*"], "rules": { diff --git a/package.json b/package.json index bab93be07..0286126b2 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "@babel/eslint-parser": "7.24.7", "@babel/parser": "7.24.7", "@chakra-ui/cli": "2.4.1", + "@eslint-react/eslint-plugin": "1.14.3", "@next/eslint-plugin-next": "14.2.4", "@playwright/test": "1.45.1", "@storybook/addon-actions": "8.1.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9662f17c2..76957849e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -201,6 +201,9 @@ importers: '@chakra-ui/cli': specifier: 2.4.1 version: 2.4.1 + '@eslint-react/eslint-plugin': + specifier: 1.14.3 + version: 1.14.3(eslint@8.57.0)(typescript@5.5.3) '@next/eslint-plugin-next': specifier: 14.2.4 version: 14.2.4 @@ -2134,6 +2137,37 @@ packages: resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-react/ast@1.14.3': + resolution: {integrity: sha512-JU0619vNfl0RaqbsyyEfLJWKupJOmf5JmHt4sCAD6Y1LCW80Pi0ZbBXeCUTdCR36mA8IJxm9PoLFliQyDYj6uw==} + + '@eslint-react/core@1.14.3': + resolution: {integrity: sha512-1T/Zubn9PSwJHNN+4SnXXPb6ZjL1ILl9hN2pkPClh8IyBoCTM+u/BHTfxI3aVF5I8yWLNDaiG7nkaxmxoBEOfQ==} + + '@eslint-react/eslint-plugin@1.14.3': + resolution: {integrity: sha512-U06zO3F56RAYXI0ZKTEpdwyWllV+muvi2gdC1SLARwk4AOmLAV8ke+iHW5EXBfNkCJQ3SgKRan4tpQqqwfEsMA==} + engines: {bun: '>=1.0.15', node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + '@eslint-react/jsx@1.14.3': + resolution: {integrity: sha512-LJqS63/S8koDJNIqKZ/yLuFvVk4RiK7K3emjUFx+UXHrIdKwFDMpFkksVVbxqeMX70E+toMXgMepABE0pA54ag==} + + '@eslint-react/shared@1.14.3': + resolution: {integrity: sha512-GP+mjNZBGXq2CuwyVTE2+74K3tBixxNeaG3ho3ovpQ7e8NlTD3TOZk5vZyOJaeRqaOJoJa54PURe12Qt6loCdw==} + + '@eslint-react/tools@1.14.3': + resolution: {integrity: sha512-NtewO4fWxzGtVCjAhD6NG4FwLev5Xq87KWpW92brPF+AvTzkr04abt3/14CpojJlW9L9SMK6FsX9tFVP7ZBqJQ==} + + '@eslint-react/types@1.14.3': + resolution: {integrity: sha512-Hi3rBCX0pAxoQs3MQYX/rt4Fxdz97U0pTjSQsm03dGUBb/BEVgrVK9SEZkCqpfPZ7NXrVhuiYudoJRUylNZyCw==} + + '@eslint-react/var@1.14.3': + resolution: {integrity: sha512-APJJVSyrDrvJn3t4qXBg1XpWMxmW5AEym56a9/ILzLtgOWFsDj6gJCy4o2g5UB2AZqtfS6YS5IfU5B1G/wYtiw==} + '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4853,6 +4887,9 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + birecord@0.1.1: + resolution: {integrity: sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -5891,12 +5928,72 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint-plugin-react-debug@1.14.3: + resolution: {integrity: sha512-qEsGT5LGFtYR1Hs9nqfrCqgE8MxrTe5VA7LO7Old8epgHgpgOGIuSIdIKYu7dxlEFGAXFB3JLW7ieYJYcgobbQ==} + engines: {bun: '>=1.0.15', node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + eslint-plugin-react-dom@1.14.3: + resolution: {integrity: sha512-tVA7RQI6Jxomeqrckqi/y1gEmcdI29b268p7K8WjRUWNUDXbZR6vEyaLBqzI8+ykO1HsK8+QhOKUHgUKHjOZBQ==} + engines: {bun: '>=1.0.15', node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + eslint-plugin-react-hooks-extra@1.14.3: + resolution: {integrity: sha512-G6mFfYiKgKbGJOUlmvcsN+n0hNiRGa9pNenv4hSlbm3TJFmlrLG+cHvOa9xe88AvaLJHfF5obgF8X/zhSekIfA==} + engines: {bun: '>=1.0.15', node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + eslint-plugin-react-hooks@4.6.0: resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint-plugin-react-naming-convention@1.14.3: + resolution: {integrity: sha512-qj7XpwYQAKNCTloWA9vPNYDRMsiLa5H/jlF3mH17Is+j/pLH97NRG9CQXbh6kEdLbBFSsHwTDvyP22+CPVZhiA==} + engines: {bun: '>=1.0.15', node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + eslint-plugin-react-web-api@1.14.3: + resolution: {integrity: sha512-1G/WIUe+ZIPW8px1lmn7ib5fy6LcuwoHDsnq9G92iE8MFXYPA0Pry0ZKaB2lAsjP8rUROv1L9B457QjyCpro2g==} + engines: {bun: '>=1.0.15', node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + eslint-plugin-react-x@1.14.3: + resolution: {integrity: sha512-VKyF4v1kWp9P6vI7JDJfonmny0HOQiS5v/rMLyldK9UC8k+efJN7dUtLE2Kt7TfxggE5gf+v4rsDB2Opvt5Tvg==} + engines: {bun: '>=1.0.15', node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + eslint-plugin-react@7.33.2: resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} engines: {node: '>=4'} @@ -6770,6 +6867,12 @@ packages: is-hexadecimal@1.0.4: resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + is-immutable-type@5.0.0: + resolution: {integrity: sha512-mcvHasqbRBWJznuPqqHRKiJgYAz60sZ0mvO3bN70JbkuK7ksfmgc489aKZYxMEjIbRvyOseaTjaRZLRF/xFeRA==} + peerDependencies: + eslint: '*' + typescript: '>=4.7.4' + is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -7494,6 +7597,10 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -7981,6 +8088,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pidtree@0.3.1: resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} engines: {node: '>=0.10'} @@ -8867,6 +8978,10 @@ packages: shell-quote@1.8.1: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + short-unique-id@5.2.0: + resolution: {integrity: sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==} + hasBin: true + side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} @@ -9049,6 +9164,9 @@ packages: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} + string-ts@2.2.0: + resolution: {integrity: sha512-VTP0LLZo4Jp9Gz5IiDVMS9WyLx/3IeYh0PXUn0NdPqusUFNgkHPWiEdbB9TU2Iv3myUskraD5WtYEdHUrQEIlQ==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -9368,6 +9486,11 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-declaration-location@1.0.4: + resolution: {integrity: sha512-r4JoxYhKULbZuH81Pjrp9OEG5St7XWk7zXwGkLKhmVcjiBVHTJXV5wK6dEa9JKW5QGSTW6b1lOjxAKp8R1SQhg==} + peerDependencies: + typescript: '>=4.0.0' + ts-dedent@2.2.0: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} @@ -9381,6 +9504,9 @@ packages: ts-pattern@5.2.0: resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} + ts-pattern@5.4.0: + resolution: {integrity: sha512-hgfOMfjlrARCnYtGD/xEAkFHDXuSyuqjzFSltyQCbN689uNvoQL20TVN2XFcLMjfNuwSsQGU+xtH6MrjIwhwUg==} + ts-pnp@1.2.0: resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==} engines: {node: '>=6'} @@ -12210,6 +12336,113 @@ snapshots: '@eslint-community/regexpp@4.10.0': {} + '@eslint-react/ast@1.14.3(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/typescript-estree': 8.8.0(typescript@5.5.3) + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + birecord: 0.1.1 + string-ts: 2.2.0 + ts-pattern: 5.4.0 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/core@1.14.3(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/jsx': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/shared': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/var': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + birecord: 0.1.1 + short-unique-id: 5.2.0 + ts-pattern: 5.4.0 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/eslint-plugin@1.14.3(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-react/shared': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + eslint: 8.57.0 + eslint-plugin-react-debug: 1.14.3(eslint@8.57.0)(typescript@5.5.3) + eslint-plugin-react-dom: 1.14.3(eslint@8.57.0)(typescript@5.5.3) + eslint-plugin-react-hooks-extra: 1.14.3(eslint@8.57.0)(typescript@5.5.3) + eslint-plugin-react-naming-convention: 1.14.3(eslint@8.57.0)(typescript@5.5.3) + eslint-plugin-react-web-api: 1.14.3(eslint@8.57.0)(typescript@5.5.3) + eslint-plugin-react-x: 1.14.3(eslint@8.57.0)(typescript@5.5.3) + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@eslint-react/jsx@1.14.3(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/var': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + ts-pattern: 5.4.0 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/shared@1.14.3(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-react/tools': 1.14.3 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + picomatch: 4.0.2 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/tools@1.14.3': {} + + '@eslint-react/types@1.14.3(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-react/tools': 1.14.3 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/var@1.14.3(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + ts-pattern: 5.4.0 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 @@ -14930,10 +15163,10 @@ snapshots: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.5 + debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.2 + semver: 7.5.4 tsutils: 3.21.0(typescript@5.5.3) optionalDependencies: typescript: 5.5.3 @@ -14944,7 +15177,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.17.0 '@typescript-eslint/visitor-keys': 6.17.0 - debug: 4.3.5 + debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -15271,7 +15504,7 @@ snapshots: agent-base@7.1.1: dependencies: - debug: 4.3.5 + debug: 4.3.4 transitivePeerDependencies: - supports-color @@ -16048,6 +16281,8 @@ snapshots: binary-extensions@2.2.0: {} + birecord@0.1.1: {} + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -17013,7 +17248,7 @@ snapshots: base64id: 2.0.0 cookie: 0.4.2 cors: 2.8.5 - debug: 4.3.5 + debug: 4.3.4 engine.io-parser: 5.0.7 ws: 8.11.0 transitivePeerDependencies: @@ -17322,10 +17557,130 @@ snapshots: object.entries: 1.1.7 object.fromentries: 2.0.7 + eslint-plugin-react-debug@1.14.3(eslint@8.57.0)(typescript@5.5.3): + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/core': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/jsx': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/shared': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/var': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + eslint: 8.57.0 + string-ts: 2.2.0 + ts-pattern: 5.4.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-dom@1.14.3(eslint@8.57.0)(typescript@5.5.3): + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/core': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/jsx': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/shared': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/var': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + eslint: 8.57.0 + ts-pattern: 5.4.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-hooks-extra@1.14.3(eslint@8.57.0)(typescript@5.5.3): + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/core': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/jsx': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/shared': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/var': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + eslint: 8.57.0 + ts-pattern: 5.4.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): dependencies: eslint: 8.57.0 + eslint-plugin-react-naming-convention@1.14.3(eslint@8.57.0)(typescript@5.5.3): + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/core': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/jsx': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/shared': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + eslint: 8.57.0 + ts-pattern: 5.4.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-web-api@1.14.3(eslint@8.57.0)(typescript@5.5.3): + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/core': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/jsx': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/shared': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/var': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + birecord: 0.1.1 + eslint: 8.57.0 + ts-pattern: 5.4.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-x@1.14.3(eslint@8.57.0)(typescript@5.5.3): + dependencies: + '@eslint-react/ast': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/core': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/jsx': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/shared': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/tools': 1.14.3 + '@eslint-react/types': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@eslint-react/var': 1.14.3(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + eslint: 8.57.0 + is-immutable-type: 5.0.0(eslint@8.57.0)(typescript@5.5.3) + ts-pattern: 5.4.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + eslint-plugin-react@7.33.2(eslint@8.57.0): dependencies: array-includes: 3.1.7 @@ -18156,7 +18511,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.4 transitivePeerDependencies: - supports-color @@ -18174,7 +18529,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.4 transitivePeerDependencies: - supports-color @@ -18367,6 +18722,16 @@ snapshots: is-hexadecimal@1.0.4: {} + is-immutable-type@5.0.0(eslint@8.57.0)(typescript@5.5.3): + dependencies: + '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.0)(typescript@5.5.3) + eslint: 8.57.0 + ts-api-utils: 1.3.0(typescript@5.5.3) + ts-declaration-location: 1.0.4(typescript@5.5.3) + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + is-interactive@1.0.0: {} is-map@2.0.2: {} @@ -19125,6 +19490,10 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} + minimatch@10.0.1: + dependencies: + brace-expansion: 2.0.1 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -19652,6 +20021,8 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.2: {} + pidtree@0.3.1: {} pidtree@0.6.0: {} @@ -20682,6 +21053,8 @@ snapshots: shell-quote@1.8.1: {} + short-unique-id@5.2.0: {} + side-channel@1.0.4: dependencies: call-bind: 1.0.5 @@ -20745,7 +21118,7 @@ snapshots: socket.io-parser@4.2.4: dependencies: '@socket.io/component-emitter': 3.1.0 - debug: 4.3.5 + debug: 4.3.4 transitivePeerDependencies: - supports-color @@ -20889,6 +21262,8 @@ snapshots: string-argv@0.3.2: {} + string-ts@2.2.0: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -21216,6 +21591,11 @@ snapshots: dependencies: typescript: 5.5.3 + ts-declaration-location@1.0.4(typescript@5.5.3): + dependencies: + minimatch: 10.0.1 + typescript: 5.5.3 + ts-dedent@2.2.0: {} ts-easing@0.2.0: {} @@ -21224,6 +21604,8 @@ snapshots: ts-pattern@5.2.0: {} + ts-pattern@5.4.0: {} + ts-pnp@1.2.0(typescript@5.5.3): optionalDependencies: typescript: 5.5.3 diff --git a/src/components/ConfirmMenuItem/index.tsx b/src/components/ConfirmMenuItem/index.tsx index e4e531ae8..76a71042e 100644 --- a/src/components/ConfirmMenuItem/index.tsx +++ b/src/components/ConfirmMenuItem/index.tsx @@ -179,7 +179,9 @@ export const ConfirmMenuItem = forwardRef( }, }, icon: icon - ? React.cloneElement(icon, { color: 'transparent' }) + ? // TODO @eslint-react rule + // eslint-disable-next-line @eslint-react/no-clone-element + React.cloneElement(icon, { color: 'transparent' }) : icon, } : {}; diff --git a/src/components/ConfirmModal/index.tsx b/src/components/ConfirmModal/index.tsx index 1ba82d0cb..0b01f1539 100644 --- a/src/components/ConfirmModal/index.tsx +++ b/src/components/ConfirmModal/index.tsx @@ -43,12 +43,16 @@ export const ConfirmModal: React.FC< !title && !message ? t('components:confirmModal.heading') : title; if (!isEnabled) { + // TODO @eslint-react rule + // eslint-disable-next-line @eslint-react/no-clone-element const childrenWithOnClick = React.cloneElement(children, { onClick: onConfirm, }); return <>{childrenWithOnClick}; } + // TODO @eslint-react rule + // eslint-disable-next-line @eslint-react/no-clone-element const childrenWithOnOpen = React.cloneElement(children, { onClick: confirmModal.onOpen, }); diff --git a/src/components/ConfirmPopover/index.tsx b/src/components/ConfirmPopover/index.tsx index 3a8da0c7e..716a084df 100644 --- a/src/components/ConfirmPopover/index.tsx +++ b/src/components/ConfirmPopover/index.tsx @@ -50,6 +50,8 @@ export const ConfirmPopover: React.FC< const initialFocusRef = useRef(null); if (!isEnabled) { + // TODO @eslint-react rule + // eslint-disable-next-line @eslint-react/no-clone-element const childrenWithOnClick = React.cloneElement(children, { onClick: onConfirm, }); diff --git a/src/components/DateAgo/index.tsx b/src/components/DateAgo/index.tsx index 93809bf80..53bc2c2b4 100644 --- a/src/components/DateAgo/index.tsx +++ b/src/components/DateAgo/index.tsx @@ -22,10 +22,10 @@ export type DateAgoProps = Omit & { }; export const DateAgo: FC> = forwardRef( - function DateAgo({ date = new Date(), format, ...rest }, ref) { + function DateAgo({ date, format, ...rest }, ref) { const { t } = useTranslation(['components']); const [, setForceUpdate] = useState(0); - const dayjsDate = dayjs(date); + const dayjsDate = dayjs(date ?? new Date()); const dateFormatted = dayjsDate.format(); useEffect(() => { diff --git a/src/components/DayPicker/_partials/DayPickerContent.tsx b/src/components/DayPicker/_partials/DayPickerContent.tsx index bcd9cec6a..857a57a8e 100644 --- a/src/components/DayPicker/_partials/DayPickerContent.tsx +++ b/src/components/DayPicker/_partials/DayPickerContent.tsx @@ -134,6 +134,8 @@ export const DayPickerContent = forwardRef< selected={value ?? undefined} onSelect={handleDaySelect} components={{ + // TODO @eslint-react rule + // eslint-disable-next-line @eslint-react/no-nested-components Caption: (props) => ( { - if (!!dateValue) { - setInputValue(dayjs(dateValue).format(dateFormat)); - } else { - setInputValue(''); - } - }, [dateFormat, dateValue]); + if (dateFormatHasChanged || dateValueHasChanged) { + setInputValue(dateValue ? dayjs(dateValue).format(dateFormat) : ''); + } const handleInputChange: ChangeEventHandler = (e) => { setInputValue(e.currentTarget.value); diff --git a/src/components/DayPicker/index.tsx b/src/components/DayPicker/index.tsx index c5c6bbbe9..78b6a1882 100644 --- a/src/components/DayPicker/index.tsx +++ b/src/components/DayPicker/index.tsx @@ -48,14 +48,14 @@ export const DayPicker: FC = ({ id, value, onChange, - onClose = () => {}, + onClose, popperPlacement = 'bottom-start', dateFormat = DATE_FORMAT, placeholder = 'JJ/MM/AAAA', - inputProps = {}, + inputProps, isDisabled = false, autoFocus = false, - onMonthChange = () => {}, + onMonthChange, ...rest }) => { const containerRef = useRef(null); @@ -69,7 +69,7 @@ export const DayPicker: FC = ({ // Popper management const onClosePopper = () => { - onClose(value); + onClose?.(value); setMode('DAY'); }; @@ -112,13 +112,13 @@ export const DayPicker: FC = ({ const { setMode, setMonth, selectMonth } = hookMonthNavigation; const handleChangeMonth = (date?: Date | null) => { - onMonthChange(date); + onMonthChange?.(date); setMonth(date); }; // Change to day view once we have selected a month on the month picker const handleSelectMonth = (date: Date) => { - onMonthChange(date); + onMonthChange?.(date); selectMonth(date); }; @@ -126,7 +126,7 @@ export const DayPicker: FC = ({ valueRef.current = value; return ( - + diff --git a/src/components/Form/form-test-utils.tsx b/src/components/Form/form-test-utils.tsx index 0e5688277..aa7092876 100644 --- a/src/components/Form/form-test-utils.tsx +++ b/src/components/Form/form-test-utils.tsx @@ -14,7 +14,7 @@ import { Form } from '.'; export const FormMocked = ({ children, schema, - useFormOptions = {}, + useFormOptions, onSubmit, }: { children(options: { form: UseFormReturn> }): ReactNode; diff --git a/src/components/Icons/docs.stories.tsx b/src/components/Icons/docs.stories.tsx index 466502301..2376ef0cf 100644 --- a/src/components/Icons/docs.stories.tsx +++ b/src/components/Icons/docs.stories.tsx @@ -32,6 +32,8 @@ const CustomIcon = ({ name: string; }) => { const { hasCopied, onCopy } = useClipboard(name); + // TODO @eslint-react rule + // eslint-disable-next-line @eslint-react/no-clone-element const icon = React.cloneElement(children, { onClick: onCopy }); return ( diff --git a/src/components/InputNumber/index.tsx b/src/components/InputNumber/index.tsx index 0a33b6457..000d2f527 100644 --- a/src/components/InputNumber/index.tsx +++ b/src/components/InputNumber/index.tsx @@ -57,7 +57,7 @@ export const InputNumber = forwardRef( max, clampValueOnBlur = true, fixedPrecision = false, - onChange = () => undefined, + onChange, placeholder, showButtons = false, inputGroupProps, diff --git a/src/components/MonthPicker/MonthPicker.tsx b/src/components/MonthPicker/MonthPicker.tsx index 4db0f4296..2c7e61994 100644 --- a/src/components/MonthPicker/MonthPicker.tsx +++ b/src/components/MonthPicker/MonthPicker.tsx @@ -13,18 +13,21 @@ interface MonthPickerProps { export const MonthPicker: React.FC< React.PropsWithChildren > = ({ - year = new Date().getFullYear(), + year, onMonthClick, onTodayButtonClick, onYearChange, - selectedMonths = [], + selectedMonths, }) => { return ( - + diff --git a/src/components/MonthPicker/MonthPickerContext.tsx b/src/components/MonthPicker/MonthPickerContext.tsx index 58198aa36..6a1187305 100644 --- a/src/components/MonthPicker/MonthPickerContext.tsx +++ b/src/components/MonthPicker/MonthPickerContext.tsx @@ -1,4 +1,4 @@ -import { createContext, useContext } from 'react'; +import { createContext, useContext, useMemo } from 'react'; type MonthPickerContextType = { onMonthClick?(month: Date): void; @@ -12,14 +12,16 @@ export const useMonthPickerContext = () => useContext(MonthPickerContext); export const MonthPickerProvider: React.FC< React.PropsWithChildren > = ({ onMonthClick, onTodayButtonClick, selectedMonths, children }) => { + const contextValue = useMemo( + () => ({ + onMonthClick, + onTodayButtonClick, + selectedMonths, + }), + [onMonthClick, onTodayButtonClick, selectedMonths] + ); return ( - + {children} ); diff --git a/src/components/MonthPicker/YearContext.tsx b/src/components/MonthPicker/YearContext.tsx index bfd87a957..2af1aff29 100644 --- a/src/components/MonthPicker/YearContext.tsx +++ b/src/components/MonthPicker/YearContext.tsx @@ -4,9 +4,12 @@ import { createContext, useContext, useEffect, + useMemo, useState, } from 'react'; +import { useValueHasChanged } from '@/hooks/useValueHasChanged'; + type YearContextType = { year: number; setYear: Dispatch>; @@ -27,23 +30,25 @@ export const YearProvider: React.FC< React.PropsWithChildren > = ({ year: yearProp, onYearChange, children }) => { const [year, setYear] = useState(yearProp); + const yearHasChanged = useValueHasChanged(yearProp); - useEffect(() => { + if (yearHasChanged && yearProp !== year) { setYear(yearProp); - }, [yearProp]); + } useEffect(() => { onYearChange?.(year); }, [onYearChange, year]); + const contextValue = useMemo( + () => ({ + year, + setYear, + }), + [year] + ); + return ( - - {children} - + {children} ); }; diff --git a/src/components/MonthPicker/docs.stories.tsx b/src/components/MonthPicker/docs.stories.tsx index 04e4b6a42..2e4b44caa 100644 --- a/src/components/MonthPicker/docs.stories.tsx +++ b/src/components/MonthPicker/docs.stories.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; -import { Box } from '@chakra-ui/react'; +import { Box, Button, Stack } from '@chakra-ui/react'; import { MonthPicker } from '.'; @@ -18,6 +18,16 @@ export const Wrapper = () => ( export const InitialYear = () => ; +export const UpdateYearFromOutside = () => { + const [year, setYear] = useState(1994); + return ( + + setYear(y)} /> + + + ); +}; + export const SelectedMonth = () => ( ); diff --git a/src/components/Nav/index.tsx b/src/components/Nav/index.tsx index c2322d36e..8c80e1ed5 100644 --- a/src/components/Nav/index.tsx +++ b/src/components/Nav/index.tsx @@ -46,8 +46,12 @@ export const Nav = ({ children, breakpoint = 'lg', ...rest }: NavProps) => { }); const [active, setActive] = useState(<>-); + const contextValue = useMemo( + () => ({ active, setActive, isMenu: !!isMenu }), + [active, isMenu] + ); return ( - + {!isMenu && ( diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx index 39eafbc89..45031d12e 100644 --- a/src/components/Pagination/index.tsx +++ b/src/components/Pagination/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, useContext } from 'react'; +import React, { FC, useContext, useMemo } from 'react'; import { Box, @@ -202,18 +202,44 @@ export const Pagination = ({ isLoadingPage = false, ...rest }: PaginationProps) => { - const pagination = getPaginationInfo({ page, pageSize, totalItems }); + const { + firstItemOnPage, + firstPage, + isFirstPage, + isLastPage, + lastItemOnPage, + lastPage, + } = getPaginationInfo({ page, pageSize, totalItems }); + const contextValue = useMemo( + () => ({ + setPage, + page, + pageSize, + totalItems, + isLoadingPage, + firstItemOnPage, + firstPage, + isFirstPage, + isLastPage, + lastItemOnPage, + lastPage, + }), + [ + isLoadingPage, + page, + pageSize, + firstItemOnPage, + firstPage, + isFirstPage, + isLastPage, + lastItemOnPage, + lastPage, + setPage, + totalItems, + ] + ); return ( - + ); diff --git a/src/components/ResponsiveIconButton/index.tsx b/src/components/ResponsiveIconButton/index.tsx index 208468d86..523901581 100644 --- a/src/components/ResponsiveIconButton/index.tsx +++ b/src/components/ResponsiveIconButton/index.tsx @@ -19,19 +19,15 @@ export const ResponsiveIconButton = forwardRef< 'button' >( ( - { - hideTextBreakpoints = { - base: true, - md: false, - }, - children, - icon, - iconPosition = 'left', - ...rest - }, + { hideTextBreakpoints, children, icon, iconPosition = 'left', ...rest }, ref ) => { - const responsiveStates = useBreakpointValue(hideTextBreakpoints); + const responsiveStates = useBreakpointValue( + hideTextBreakpoints ?? { + base: true, + md: false, + } + ); const buttonProps = iconPosition === 'right' ? { rightIcon: icon } : { leftIcon: icon }; diff --git a/src/components/SearchInput/docs.stories.tsx b/src/components/SearchInput/docs.stories.tsx index cfd121073..35c92c19f 100644 --- a/src/components/SearchInput/docs.stories.tsx +++ b/src/components/SearchInput/docs.stories.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; -import { Stack, Text } from '@chakra-ui/react'; +import { Button, Stack, Text } from '@chakra-ui/react'; import { SearchInput } from '.'; @@ -19,6 +19,9 @@ export const Controlled = () => { {value} + ); }; diff --git a/src/components/SearchInput/index.tsx b/src/components/SearchInput/index.tsx index 28fa8623f..eef6405ee 100644 --- a/src/components/SearchInput/index.tsx +++ b/src/components/SearchInput/index.tsx @@ -14,6 +14,8 @@ import { import { useTranslation } from 'react-i18next'; import { LuSearch, LuX } from 'react-icons/lu'; +import { useValueHasChanged } from '@/hooks/useValueHasChanged'; + type CustomProps = { value?: string; defaultValue?: string; @@ -64,11 +66,10 @@ export const SearchInput = forwardRef( return () => clearTimeout(handler); }, [search, delay]); - useEffect(() => { - if (externalValue !== searchRef.current) { - setSearch(externalValue); - } - }, [externalValue]); + const externalValueHasChanged = useValueHasChanged(externalValue); + if (externalValueHasChanged && externalValue !== searchRef.current) { + setSearch(externalValue); + } const handleChange = (event: React.ChangeEvent) => { setSearch(event.target.value); diff --git a/src/components/Sort/index.tsx b/src/components/Sort/index.tsx index 15d554419..8efe37714 100644 --- a/src/components/Sort/index.tsx +++ b/src/components/Sort/index.tsx @@ -39,12 +39,12 @@ type CustomProps = { type SortProps = Overwrite; export const Sort: FC> = ({ - sort = { by: '', order: 'asc' }, + sort, size = 'xs', - options = [], - onChange = () => undefined, - ascIcon = , - descIcon = , + options, + onChange, + ascIcon, + descIcon, ...rest }) => { const { t } = useTranslation(['components']); @@ -52,11 +52,11 @@ export const Sort: FC> = ({ const { by, order } = sort; const handleByChange = (value: SortValue['by']) => { - onChange({ ...sort, by: value }); + onChange?.({ ...sort, by: value }); }; const handleOrderChange = (value: SortValue['order']) => { - onChange({ ...sort, order: value }); + onChange?.({ ...sort, order: value }); }; return ( @@ -77,7 +77,9 @@ export const Sort: FC> = ({ {...rest} > - {order === 'asc' ? ascIcon : descIcon} + {order === 'asc' + ? ascIcon ?? + : descIcon ?? } > = ({ fontSize={size} noOfLines={1} > - {options.find((option) => option?.value === by)?.label} + {options?.find((option) => option?.value === by)?.label} diff --git a/src/features/admin/AdminNavBar.tsx b/src/features/admin/AdminNavBar.tsx index b6a9c5712..bf3615804 100644 --- a/src/features/admin/AdminNavBar.tsx +++ b/src/features/admin/AdminNavBar.tsx @@ -322,6 +322,7 @@ const AdminNavBarAccountMenuVersion = ({ ...rest }) => { undefined, + onSuccess, buttonVariant = '@primary', ...rest }: LoginFormProps) => { diff --git a/src/hooks/useIsHydrated.ts b/src/hooks/useIsHydrated.ts index ab796dca3..ed1bd7720 100644 --- a/src/hooks/useIsHydrated.ts +++ b/src/hooks/useIsHydrated.ts @@ -8,6 +8,8 @@ import { useEffect, useState } from 'react'; export const useIsHydrated = () => { const [isHydrated, setIsHydrated] = useState(false); useEffect(() => { + // TODO @eslint-react rule + // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect setIsHydrated(true); }, []); return isHydrated; diff --git a/src/hooks/useValueHasChanged.ts b/src/hooks/useValueHasChanged.ts new file mode 100644 index 000000000..2f57abeaa --- /dev/null +++ b/src/hooks/useValueHasChanged.ts @@ -0,0 +1,9 @@ +import { useRef } from 'react'; + +export const useValueHasChanged = (value: unknown) => { + const valueRef = useRef(value); + const valueHasChanged = valueRef.current !== value; + valueRef.current = value; + + return valueHasChanged; +}; diff --git a/src/lib/i18n/useLocale.ts b/src/lib/i18n/useLocale.ts index 13aef7a97..2194e6a59 100644 --- a/src/lib/i18n/useLocale.ts +++ b/src/lib/i18n/useLocale.ts @@ -12,6 +12,8 @@ export const useLocale = () => { }>(); useEffect(() => { + // TODO @eslint-react rule + // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect setLocale({ lang: i18n.language, dir: