diff --git a/package-lock.json b/package-lock.json
index cc2db5b..78e4175 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,17 +8,24 @@
"name": "test-components",
"version": "0.1.0",
"dependencies": {
+ "classnames": "^2.5.1",
+ "lru-cache": "^11.2.7",
"next": "16.1.6",
+ "rate-limiter-flexible": "^10.0.1",
"react": "19.2.3",
- "react-dom": "19.2.3"
+ "react-dom": "19.2.3",
+ "sass": "^1.98.0"
},
"devDependencies": {
+ "@faker-js/faker": "^10.3.0",
"@types/node": "^20",
+ "@types/prismjs": "^1.26.6",
"@types/react": "^19",
"@types/react-dom": "^19",
"babel-plugin-react-compiler": "1.0.0",
"eslint": "^9",
"eslint-config-next": "16.1.6",
+ "prismjs": "^1.30.0",
"typescript": "^5"
}
},
@@ -112,6 +119,16 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
@@ -439,6 +456,23 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@faker-js/faker": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-10.4.0.tgz",
+ "integrity": "sha512-sDBWI3yLy8EcDzgobvJTWq1MJYzAkQdpjXuPukga9wXonhpMRvd1Izuo2Qgwey2OiEoRIBr35RMU9HJRoOHzpw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fakerjs"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0",
+ "npm": ">=10"
+ }
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -1212,6 +1246,315 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@parcel/watcher": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
+ "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^2.0.3",
+ "is-glob": "^4.0.3",
+ "node-addon-api": "^7.0.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher-android-arm64": "2.5.6",
+ "@parcel/watcher-darwin-arm64": "2.5.6",
+ "@parcel/watcher-darwin-x64": "2.5.6",
+ "@parcel/watcher-freebsd-x64": "2.5.6",
+ "@parcel/watcher-linux-arm-glibc": "2.5.6",
+ "@parcel/watcher-linux-arm-musl": "2.5.6",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.6",
+ "@parcel/watcher-linux-arm64-musl": "2.5.6",
+ "@parcel/watcher-linux-x64-glibc": "2.5.6",
+ "@parcel/watcher-linux-x64-musl": "2.5.6",
+ "@parcel/watcher-win32-arm64": "2.5.6",
+ "@parcel/watcher-win32-ia32": "2.5.6",
+ "@parcel/watcher-win32-x64": "2.5.6"
+ }
+ },
+ "node_modules/@parcel/watcher-android-arm64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
+ "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-arm64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
+ "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-x64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
+ "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-freebsd-x64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
+ "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-glibc": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
+ "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-musl": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
+ "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-glibc": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
+ "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-musl": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
+ "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-glibc": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
+ "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-musl": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
+ "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-arm64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
+ "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-ia32": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
+ "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-x64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
+ "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher/node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -1270,6 +1613,13 @@
"undici-types": "~6.21.0"
}
},
+ "node_modules/@types/prismjs": {
+ "version": "1.26.6",
+ "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz",
+ "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/react": {
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
@@ -2324,6 +2674,27 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+ "license": "MIT"
+ },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@@ -3640,6 +4011,12 @@
"node": ">= 4"
}
},
+ "node_modules/immutable": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz",
+ "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==",
+ "license": "MIT"
+ },
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -3844,7 +4221,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -3890,7 +4267,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -4293,13 +4670,12 @@
}
},
"node_modules/lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^3.0.2"
+ "version": "11.2.7",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+ "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
}
},
"node_modules/math-intrinsics": {
@@ -4460,6 +4836,13 @@
}
}
},
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "license": "MIT",
+ "optional": true
+ },
"node_modules/node-exports-info": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz",
@@ -4784,6 +5167,16 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prismjs": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+ "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -4827,6 +5220,12 @@
],
"license": "MIT"
},
+ "node_modules/rate-limiter-flexible": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-10.0.1.tgz",
+ "integrity": "sha512-3G6GMFz5Oz5nVnDv9gQ1LLMdExR4B1lOjogPIjehtgyxPMIkY09BGyk2eCYt36/OkV/0t12GEt6J6HpTl6RzZg==",
+ "license": "ISC"
+ },
"node_modules/react": {
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
@@ -4855,6 +5254,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -5030,6 +5442,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/sass": {
+ "version": "1.98.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.98.0.tgz",
+ "integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==",
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^4.0.0",
+ "immutable": "^5.1.5",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher": "^2.4.1"
+ }
+ },
"node_modules/scheduler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
diff --git a/package.json b/package.json
index f9aa143..5a86b17 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,9 @@
},
"dependencies": {
"classnames": "^2.5.1",
+ "lru-cache": "^11.2.7",
"next": "16.1.6",
+ "rate-limiter-flexible": "^10.0.1",
"react": "19.2.3",
"react-dom": "19.2.3",
"sass": "^1.98.0"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7dfb337..95dd817 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,9 +11,15 @@ importers:
classnames:
specifier: ^2.5.1
version: 2.5.1
+ lru-cache:
+ specifier: ^11.2.7
+ version: 11.2.7
next:
specifier: 16.1.6
version: 16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.98.0)
+ rate-limiter-flexible:
+ specifier: ^10.0.1
+ version: 10.0.1
react:
specifier: 19.2.3
version: 19.2.3
@@ -221,105 +227,89 @@ packages:
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
- libc: [musl]
'@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
@@ -386,28 +376,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@next/swc-linux-arm64-musl@16.1.6':
resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@next/swc-linux-x64-gnu@16.1.6':
resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@next/swc-linux-x64-musl@16.1.6':
resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@next/swc-win32-arm64-msvc@16.1.6':
resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==}
@@ -466,42 +452,36 @@ packages:
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.6':
resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
- libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.6':
resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.6':
resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.6':
resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.6':
resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@parcel/watcher-win32-arm64@2.5.6':
resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
@@ -655,49 +635,41 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
- libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -1432,6 +1404,10 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
+ lru-cache@11.2.7:
+ resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==}
+ engines: {node: 20 || >=22}
+
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@@ -1604,6 +1580,9 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ rate-limiter-flexible@10.0.1:
+ resolution: {integrity: sha512-3G6GMFz5Oz5nVnDv9gQ1LLMdExR4B1lOjogPIjehtgyxPMIkY09BGyk2eCYt36/OkV/0t12GEt6J6HpTl6RzZg==}
+
react-dom@19.2.3:
resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==}
peerDependencies:
@@ -3367,6 +3346,8 @@ snapshots:
dependencies:
js-tokens: 4.0.0
+ lru-cache@11.2.7: {}
+
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
@@ -3539,6 +3520,8 @@ snapshots:
queue-microtask@1.2.3: {}
+ rate-limiter-flexible@10.0.1: {}
+
react-dom@19.2.3(react@19.2.3):
dependencies:
react: 19.2.3
diff --git a/src/app/_posts-generator/page.tsx b/src/app/_posts-generator/page.tsx
new file mode 100644
index 0000000..dbb43cd
--- /dev/null
+++ b/src/app/_posts-generator/page.tsx
@@ -0,0 +1,65 @@
+"use client";
+import { UsersGeneratorContext } from "./template";
+import { useContext, useMemo } from "react";
+import SwitchSection from "@/components/switch-section";
+import { useFetchPosts } from "@/hooks/useFetchPosts";
+import { Button } from "@/components/ui";
+import * as Prism from "prismjs";
+import "prismjs/components/prism-json";
+import "prismjs/components/prism-sql";
+import buildQuery from "@/utilities/buildQuery";
+
+export default function UsersGeneratorPage() {
+ const {
+ count,
+ seed,
+ } = useContext(UsersGeneratorContext);
+
+ const { isLoading, refetch, data } = useFetchPosts();
+
+ const highlightedJson = useMemo(() => {
+ if (!data) return "[]";
+ if (!data.ok) return Prism.highlight(data.error, Prism.languages.json, "json");
+ const jsonString = JSON.stringify(data.value, null, 2);
+ return Prism.highlight(jsonString, Prism.languages.json, "json");
+ }, [data]);
+
+ const highlightedSql = useMemo(() => {
+ if (!data) return "";
+ if (!data.ok) return Prism.highlight(data.error, Prism.languages.json, "json");
+ const sqlString = buildQuery(data);
+ return Prism.highlight(sqlString, Prism.languages.sql, "sql");
+ }, [data]);
+
+ const handleGenerate = () => {
+ refetch({ count, seed });
+ }
+
+ return (
+ <>
+
Random posts generator
+
+ Generate
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/_posts-generator/template.tsx b/src/app/_posts-generator/template.tsx
new file mode 100644
index 0000000..c3b2213
--- /dev/null
+++ b/src/app/_posts-generator/template.tsx
@@ -0,0 +1,40 @@
+"use client";
+import { ReactNode, createContext, useState } from "react";
+import Sidebar from "@/components/sidebar";
+import { Input } from "@/components/ui";
+import { cn } from "@/utilities";
+import { PostArgs } from "@/types/generator";
+
+export const UsersGeneratorContext = createContext({
+ count: 10,
+ seed: undefined,
+});
+
+export default function UsersGeneratorTemplate({ children }: { children: ReactNode }) {
+ const [count, setCount] = useState(10);
+ const [seed, setSeed] = useState(undefined);
+
+ return (
+ <>
+
+
+ Settings
+
+
Count
+
+ ) => setCount(Number(e.target.value) ? Number(e.target.value) : 0)} />
+
+
+
Seed
+
+ ) => setSeed(Number(e.target.value) ? Number(e.target.value) : undefined)} />
+
+
+
+
+ {children}
+
+
+ >
+ );
+}
diff --git a/src/app/api/generate/route.ts b/src/app/api/generate/route.ts
deleted file mode 100644
index 64fd691..0000000
--- a/src/app/api/generate/route.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-"use server";
-import { NextRequest, NextResponse } from "next/server";
-import { generateData } from "@/utilities/dataGenerator";
-import { GeneratorResponse, GeneratorArgs } from "@/types/generator";
-
-export async function POST(request: NextRequest): Promise {
- try {
- const body = await request.json();
- const { count, fields, seed }: GeneratorArgs = body;
- if (typeof count !== "number" || count < 1 || count > 300) {
- return NextResponse.json({ error: "Invalid count, must be between 1 and 300." }, { status: 400 });
- }
-
- if (typeof fields !== "number" && !Array.isArray(fields)) {
- return NextResponse.json({ error: "Invalid fields format." }, { status: 400 });
- }
-
- let message = "";
-
- const { result, data } = await generateData({ count, fields, seed });
- if (result == 0) {
- message = "Data generated successfully.";
- } else if (result == 1) {
- return NextResponse.json({ error: "Invalid fields selected." }, { status: 400 });
- } else {
- return NextResponse.json({ error: "Invalid count, must be between 1 and 300." }, { status: 400 });
- }
-
- const response: GeneratorResponse = {
- meta: {
- count,
- generatedAt: new Date().toISOString(),
- message,
- },
- data,
- };
-
- return NextResponse.json(response);
- } catch (_error) {
- return NextResponse.json({ error: "Internal server error" }, { status: 500 });
- }
-}
diff --git a/src/app/api/generator/posts/route.ts b/src/app/api/generator/posts/route.ts
new file mode 100644
index 0000000..dc5a656
--- /dev/null
+++ b/src/app/api/generator/posts/route.ts
@@ -0,0 +1,27 @@
+import { NextRequest, NextResponse } from "next/server";
+import { PostArgs } from "@/types/generator";
+import generatePosts from "@/utilities/postsGenerator";
+import { checkRateLimit } from "@/utilities/ratelimit/rateLimiter";
+
+export async function POST(request: NextRequest): Promise {
+ try {
+ const rateLimit = await checkRateLimit(request);
+ if (!rateLimit.success) {
+ return NextResponse.json({ error: "Too many requests" }, { status: 429, headers: rateLimit.headers });
+ }
+
+ const body = await request.json();
+ const { count, seed }: PostArgs = body;
+
+ const response = await generatePosts({ count, seed });
+
+ if (!response.ok) {
+ return NextResponse.json(response, { status: 400, headers: rateLimit.headers });
+ }
+
+ return NextResponse.json(response, { headers: rateLimit.headers });
+ } catch (error) {
+ console.error(error);
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 });
+ }
+}
diff --git a/src/app/api/generator/users/route.ts b/src/app/api/generator/users/route.ts
new file mode 100644
index 0000000..9b22288
--- /dev/null
+++ b/src/app/api/generator/users/route.ts
@@ -0,0 +1,27 @@
+import { NextRequest, NextResponse } from "next/server";
+import generateData from "@/utilities/dataGenerator";
+import { UserArgs } from "@/types/generator";
+import { checkRateLimit } from "@/utilities/ratelimit/rateLimiter";
+
+export async function POST(request: NextRequest) {
+ try {
+ const rateLimit = await checkRateLimit(request);
+ if (!rateLimit.success) {
+ return NextResponse.json({ error: "Too many requests" }, { status: 429, headers: rateLimit.headers });
+ }
+
+ const body = await request.json();
+ const { count, fields, seed }: UserArgs = body;
+
+ const response = await generateData({ count, fields, seed });
+
+ if (!response.ok) {
+ return NextResponse.json(response, { status: 400, headers: rateLimit.headers });
+ }
+
+ return NextResponse.json(response, { headers: rateLimit.headers });
+ } catch (error) {
+ console.error(error);
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 });
+ }
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 9eddaf4..65a1806 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,7 +1,7 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import "./styles/default.scss";
-import "prismjs/themes/prism-tomorrow.css";
+import "prismjs/themes/prism-solarizedlight.css";
import Header from "@/components/header";
export const metadata: Metadata = {
@@ -14,10 +14,8 @@ export default function Layout({ children }: { children: ReactNode }) {
-
- {children}
-
+ {children}
- )
+ );
}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index a927aed..bd36ecd 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -8,26 +8,41 @@ export default function HomePage() {
Primary section.
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate ad, fuga facilis cum possimus nostrum id voluptates. Doloremque quibusdam consectetur, enim ut neque facere commodi eius at totam magnam quae.
- Nam optio quibusdam libero distinctio reprehenderit officia ex eum quasi velit, impedit vero. Possimus, fugiat. Eveniet quasi labore molestiae enim, ullam dolore recusandae amet nihil quas quaerat obcaecati pariatur officiis.
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate ad, fuga facilis cum possimus nostrum id
+ voluptates. Doloremque quibusdam consectetur, enim ut neque facere commodi eius at totam magnam quae. Nam
+ optio quibusdam libero distinctio reprehenderit officia ex eum quasi velit, impedit vero. Possimus, fugiat.
+ Eveniet quasi labore molestiae enim, ullam dolore recusandae amet nihil quas quaerat obcaecati pariatur
+ officiis.
+
Secondary section.
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate ad, fuga facilis cum possimus nostrum id voluptates. Doloremque quibusdam consectetur, enim ut neque facere commodi eius at totam magnam quae.
- Nam optio quibusdam libero distinctio reprehenderit officia ex eum quasi velit, impedit vero. Possimus, fugiat. Eveniet quasi labore molestiae enim, ullam dolore recusandae amet nihil quas quaerat obcaecati pariatur officiis.
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate ad, fuga facilis cum possimus nostrum id
+ voluptates. Doloremque quibusdam consectetur, enim ut neque facere commodi eius at totam magnam quae. Nam
+ optio quibusdam libero distinctio reprehenderit officia ex eum quasi velit, impedit vero. Possimus, fugiat.
+ Eveniet quasi labore molestiae enim, ullam dolore recusandae amet nihil quas quaerat obcaecati pariatur
+ officiis.
+
Disabled section.
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate ad, fuga facilis cum possimus nostrum id voluptates. Doloremque quibusdam consectetur, enim ut neque facere commodi eius at totam magnam quae.
- Nam optio quibusdam libero distinctio reprehenderit officia ex eum quasi velit, impedit vero. Possimus, fugiat. Eveniet quasi labore molestiae enim, ullam dolore recusandae amet nihil quas quaerat obcaecati pariatur officiis.
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate ad, fuga facilis cum possimus nostrum id
+ voluptates. Doloremque quibusdam consectetur, enim ut neque facere commodi eius at totam magnam quae. Nam
+ optio quibusdam libero distinctio reprehenderit officia ex eum quasi velit, impedit vero. Possimus, fugiat.
+ Eveniet quasi labore molestiae enim, ullam dolore recusandae amet nihil quas quaerat obcaecati pariatur
+ officiis.
+
>
- )
+ );
}
diff --git a/src/app/styles/_config.scss b/src/app/styles/_config.scss
index 67aae2c..ca24d1f 100644
--- a/src/app/styles/_config.scss
+++ b/src/app/styles/_config.scss
@@ -1,17 +1,17 @@
$props: (
- color-gray-0: #FFFFFF,
- color-gray-25: #FDFDFD,
- color-gray-50: #FAFAFA,
- color-gray-100: #F5F5F5,
- color-gray-200: #EAEAEA,
- color-gray-300: #D7D7D7,
- color-gray-400: #A6A6A6,
+ color-gray-0: #ffffff,
+ color-gray-25: #fdfdfd,
+ color-gray-50: #fafafa,
+ color-gray-100: #f5f5f5,
+ color-gray-200: #eaeaea,
+ color-gray-300: #d7d7d7,
+ color-gray-400: #a6a6a6,
color-gray-500: #777777,
- color-gray-600: #5A5A5A,
+ color-gray-600: #5a5a5a,
color-gray-700: #484848,
- color-gray-800: #2D2D2D,
- color-gray-900: #1F1F1F,
- color-gray-950: #0E0E0E,
+ color-gray-800: #2d2d2d,
+ color-gray-900: #1f1f1f,
+ color-gray-950: #0e0e0e,
color-gray-1000: #000000,
color-brand-primary: #007bff,
@@ -21,18 +21,17 @@ $props: (
color-bg-secondary: light-dark(var(--color-gray-200), var(--color-gray-900)),
color-bg-secondary-inverse: light-dark(var(--color-gray-900), var(--color-gray-200)),
color-bg-disabled: light-dark(var(--color-gray-300), var(--color-gray-700)),
-
color-fg-lighter: light-dark(var(--color-gray-1000), var(--color-gray-0)),
color-fg-primary: light-dark(var(--color-gray-800), var(--color-gray-200)),
color-fg-inverse: light-dark(var(--color-gray-0), var(--color-gray-900)),
color-fg-secondary: light-dark(var(--color-gray-600), var(--color-gray-400)),
color-fg-secondary-inverse: light-dark(var(--color-gray-400), var(--color-gray-600)),
color-fg-disabled: light-dark(var(--color-gray-500), var(--color-gray-400)),
-
box-shadow-primary: light-dark(0 0 15px 1px rgba(0, 0, 0, 0.25), 0 0 15px 1px rgba(255, 255, 255, 0.25)),
- box-shadow-focus: (0 0 0px 2px var(--color-bg-primary),
- 0 0 0px 4px color-mix(in oklab, var(--color-brand-primary), var(--color-bg-primary))),
-
+ box-shadow-focus: (
+ 0 0 0px 2px var(--color-bg-primary),
+ 0 0 0px 4px color-mix(in oklab, var(--color-brand-primary), var(--color-bg-primary)),
+ ),
size-xxxs: 8px,
size-xxs: 16px,
size-xs: 24px,
diff --git a/src/app/styles/_open-props.scss b/src/app/styles/_open-props.scss
index 696b16b..eaeeef1 100644
--- a/src/app/styles/_open-props.scss
+++ b/src/app/styles/_open-props.scss
@@ -7,9 +7,14 @@
--font-classical-humanist: Optima, Candara, Noto Sans, source-sans-pro, sans-serif;
--font-neo-grotesque: Inter, Roboto, Helvetica Neue, Arial Nova, Nimbus Sans, Arial, sans-serif;
--font-monospace-slab-serif: Nimbus Mono PS, Courier New, monospace;
- --font-monospace-code: Dank Mono, Operator Mono, Inconsolata, Fira Mono, ui-monospace, SF Mono, Monaco, Droid Sans Mono, Source Code Pro, Cascadia Code, Menlo, Consolas, DejaVu Sans Mono, monospace;
- --font-industrial: Bahnschrift, DIN Alternate, Franklin Gothic Medium, Nimbus Sans Narrow, sans-serif-condensed, sans-serif;
- --font-rounded-sans: ui-rounded, Hiragino Maru Gothic ProN, Quicksand, Comfortaa, Manjari, Arial Rounded MT, Arial Rounded MT Bold, Calibri, source-sans-pro, sans-serif;
+ --font-monospace-code:
+ Dank Mono, Operator Mono, Inconsolata, Fira Mono, ui-monospace, SF Mono, Monaco, Droid Sans Mono, Source Code Pro,
+ Cascadia Code, Menlo, Consolas, DejaVu Sans Mono, monospace;
+ --font-industrial:
+ Bahnschrift, DIN Alternate, Franklin Gothic Medium, Nimbus Sans Narrow, sans-serif-condensed, sans-serif;
+ --font-rounded-sans:
+ ui-rounded, Hiragino Maru Gothic ProN, Quicksand, Comfortaa, Manjari, Arial Rounded MT, Arial Rounded MT Bold,
+ Calibri, source-sans-pro, sans-serif;
--font-slab-serif: Rockwell, Rockwell Nova, Roboto Slab, DejaVu Serif, Sitka Small, serif;
--font-antique: Superclarendon, Bookman Old Style, URW Bookman, URW Bookman L, Georgia Pro, Georgia, serif;
--font-didone: Didot, Bodoni MT, Noto Serif Display, URW Palladio L, P052, Sylfaen, serif;
@@ -26,23 +31,23 @@
--font-weight-7: 700;
--font-weight-8: 800;
--font-weight-9: 900;
- --font-lineheight-00: .95;
+ --font-lineheight-00: 0.95;
--font-lineheight-0: 1.1;
--font-lineheight-1: 1.25;
--font-lineheight-2: 1.375;
--font-lineheight-3: 1.5;
--font-lineheight-4: 1.75;
--font-lineheight-5: 2;
- --font-letterspacing-0: -.05em;
- --font-letterspacing-1: .025em;
- --font-letterspacing-2: .050em;
- --font-letterspacing-3: .075em;
- --font-letterspacing-4: .150em;
- --font-letterspacing-5: .500em;
- --font-letterspacing-6: .750em;
+ --font-letterspacing-0: -0.05em;
+ --font-letterspacing-1: 0.025em;
+ --font-letterspacing-2: 0.05em;
+ --font-letterspacing-3: 0.075em;
+ --font-letterspacing-4: 0.15em;
+ --font-letterspacing-5: 0.5em;
+ --font-letterspacing-6: 0.75em;
--font-letterspacing-7: 1em;
- --font-size-00: .5rem;
- --font-size-0: .75rem;
+ --font-size-00: 0.5rem;
+ --font-size-0: 0.75rem;
--font-size-1: 1rem;
--font-size-2: 1.1rem;
--font-size-3: 1.25rem;
@@ -51,14 +56,14 @@
--font-size-6: 2.5rem;
--font-size-7: 3rem;
--font-size-8: 3.5rem;
- --font-size-fluid-0: max(.75rem, min(2vw, 1rem));
+ --font-size-fluid-0: max(0.75rem, min(2vw, 1rem));
--font-size-fluid-1: max(1rem, min(4vw, 1.5rem));
--font-size-fluid-2: max(1.5rem, min(6vw, 2.5rem));
--font-size-fluid-3: max(2rem, min(9vw, 3.5rem));
- --size-000: -.5rem;
- --size-00: -.25rem;
- --size-1: .25rem;
- --size-2: .5rem;
+ --size-000: -0.5rem;
+ --size-00: -0.25rem;
+ --size-1: 0.25rem;
+ --size-2: 0.5rem;
--size-3: 1rem;
--size-4: 1.25rem;
--size-5: 1.5rem;
@@ -89,7 +94,7 @@
--size-px-13: 240px;
--size-px-14: 320px;
--size-px-15: 480px;
- --size-fluid-1: max(.5rem, min(1vw, 1rem));
+ --size-fluid-1: max(0.5rem, min(1vw, 1rem));
--size-fluid-2: max(1rem, min(2vw, 1.5rem));
--size-fluid-3: max(1.5rem, min(3vw, 2rem));
--size-fluid-4: max(2rem, min(4vw, 3rem));
@@ -112,10 +117,10 @@
--size-lg: 1024px;
--size-xl: 1440px;
--size-xxl: 1920px;
- --size-relative-000: -.5ch;
- --size-relative-00: -.25ch;
- --size-relative-1: .25ch;
- --size-relative-2: .5ch;
+ --size-relative-000: -0.5ch;
+ --size-relative-00: -0.25ch;
+ --size-relative-1: 0.25ch;
+ --size-relative-2: 0.5ch;
--size-relative-3: 1ch;
--size-relative-4: 1.25ch;
--size-relative-5: 1.5ch;
@@ -129,41 +134,41 @@
--size-relative-13: 15ch;
--size-relative-14: 20ch;
--size-relative-15: 30ch;
- --ease-1: cubic-bezier(.25, 0, .5, 1);
- --ease-2: cubic-bezier(.25, 0, .4, 1);
- --ease-3: cubic-bezier(.25, 0, .3, 1);
- --ease-4: cubic-bezier(.25, 0, .2, 1);
- --ease-5: cubic-bezier(.25, 0, .1, 1);
- --ease-in-1: cubic-bezier(.25, 0, 1, 1);
- --ease-in-2: cubic-bezier(.50, 0, 1, 1);
- --ease-in-3: cubic-bezier(.70, 0, 1, 1);
- --ease-in-4: cubic-bezier(.90, 0, 1, 1);
+ --ease-1: cubic-bezier(0.25, 0, 0.5, 1);
+ --ease-2: cubic-bezier(0.25, 0, 0.4, 1);
+ --ease-3: cubic-bezier(0.25, 0, 0.3, 1);
+ --ease-4: cubic-bezier(0.25, 0, 0.2, 1);
+ --ease-5: cubic-bezier(0.25, 0, 0.1, 1);
+ --ease-in-1: cubic-bezier(0.25, 0, 1, 1);
+ --ease-in-2: cubic-bezier(0.5, 0, 1, 1);
+ --ease-in-3: cubic-bezier(0.7, 0, 1, 1);
+ --ease-in-4: cubic-bezier(0.9, 0, 1, 1);
--ease-in-5: cubic-bezier(1, 0, 1, 1);
- --ease-out-1: cubic-bezier(0, 0, .75, 1);
- --ease-out-2: cubic-bezier(0, 0, .50, 1);
- --ease-out-3: cubic-bezier(0, 0, .3, 1);
- --ease-out-4: cubic-bezier(0, 0, .1, 1);
+ --ease-out-1: cubic-bezier(0, 0, 0.75, 1);
+ --ease-out-2: cubic-bezier(0, 0, 0.5, 1);
+ --ease-out-3: cubic-bezier(0, 0, 0.3, 1);
+ --ease-out-4: cubic-bezier(0, 0, 0.1, 1);
--ease-out-5: cubic-bezier(0, 0, 0, 1);
- --ease-in-out-1: cubic-bezier(.1, 0, .9, 1);
- --ease-in-out-2: cubic-bezier(.3, 0, .7, 1);
- --ease-in-out-3: cubic-bezier(.5, 0, .5, 1);
- --ease-in-out-4: cubic-bezier(.7, 0, .3, 1);
- --ease-in-out-5: cubic-bezier(.9, 0, .1, 1);
- --ease-elastic-out-1: cubic-bezier(.5, .75, .75, 1.25);
- --ease-elastic-out-2: cubic-bezier(.5, 1, .75, 1.25);
- --ease-elastic-out-3: cubic-bezier(.5, 1.25, .75, 1.25);
- --ease-elastic-out-4: cubic-bezier(.5, 1.5, .75, 1.25);
- --ease-elastic-out-5: cubic-bezier(.5, 1.75, .75, 1.25);
- --ease-elastic-in-1: cubic-bezier(.5, -0.25, .75, 1);
- --ease-elastic-in-2: cubic-bezier(.5, -0.50, .75, 1);
- --ease-elastic-in-3: cubic-bezier(.5, -0.75, .75, 1);
- --ease-elastic-in-4: cubic-bezier(.5, -1.00, .75, 1);
- --ease-elastic-in-5: cubic-bezier(.5, -1.25, .75, 1);
- --ease-elastic-in-out-1: cubic-bezier(.5, -.1, .1, 1.5);
- --ease-elastic-in-out-2: cubic-bezier(.5, -.3, .1, 1.5);
- --ease-elastic-in-out-3: cubic-bezier(.5, -.5, .1, 1.5);
- --ease-elastic-in-out-4: cubic-bezier(.5, -.7, .1, 1.5);
- --ease-elastic-in-out-5: cubic-bezier(.5, -.9, .1, 1.5);
+ --ease-in-out-1: cubic-bezier(0.1, 0, 0.9, 1);
+ --ease-in-out-2: cubic-bezier(0.3, 0, 0.7, 1);
+ --ease-in-out-3: cubic-bezier(0.5, 0, 0.5, 1);
+ --ease-in-out-4: cubic-bezier(0.7, 0, 0.3, 1);
+ --ease-in-out-5: cubic-bezier(0.9, 0, 0.1, 1);
+ --ease-elastic-out-1: cubic-bezier(0.5, 0.75, 0.75, 1.25);
+ --ease-elastic-out-2: cubic-bezier(0.5, 1, 0.75, 1.25);
+ --ease-elastic-out-3: cubic-bezier(0.5, 1.25, 0.75, 1.25);
+ --ease-elastic-out-4: cubic-bezier(0.5, 1.5, 0.75, 1.25);
+ --ease-elastic-out-5: cubic-bezier(0.5, 1.75, 0.75, 1.25);
+ --ease-elastic-in-1: cubic-bezier(0.5, -0.25, 0.75, 1);
+ --ease-elastic-in-2: cubic-bezier(0.5, -0.5, 0.75, 1);
+ --ease-elastic-in-3: cubic-bezier(0.5, -0.75, 0.75, 1);
+ --ease-elastic-in-4: cubic-bezier(0.5, -1, 0.75, 1);
+ --ease-elastic-in-5: cubic-bezier(0.5, -1.25, 0.75, 1);
+ --ease-elastic-in-out-1: cubic-bezier(0.5, -0.1, 0.1, 1.5);
+ --ease-elastic-in-out-2: cubic-bezier(0.5, -0.3, 0.1, 1.5);
+ --ease-elastic-in-out-3: cubic-bezier(0.5, -0.5, 0.1, 1.5);
+ --ease-elastic-in-out-4: cubic-bezier(0.5, -0.7, 0.1, 1.5);
+ --ease-elastic-in-out-5: cubic-bezier(0.5, -0.9, 0.1, 1.5);
--ease-step-1: steps(2);
--ease-step-2: steps(3);
--ease-step-3: steps(4);
@@ -179,37 +184,339 @@
--ease-squish-3: var(--ease-elastic-in-out-3);
--ease-squish-4: var(--ease-elastic-in-out-4);
--ease-squish-5: var(--ease-elastic-in-out-5);
- --ease-spring-1: linear(0, 0.006, 0.025 2.8%, 0.101 6.1%, 0.539 18.9%, 0.721 25.3%, 0.849 31.5%, 0.937 38.1%, 0.968 41.8%, 0.991 45.7%, 1.006 50.1%, 1.015 55%, 1.017 63.9%, 1.001);
- --ease-spring-2: linear(0, 0.007, 0.029 2.2%, 0.118 4.7%, 0.625 14.4%, 0.826 19%, 0.902, 0.962, 1.008 26.1%, 1.041 28.7%, 1.064 32.1%, 1.07 36%, 1.061 40.5%, 1.015 53.4%, 0.999 61.6%, 0.995 71.2%, 1);
- --ease-spring-3: linear(0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%, 0.938 16.7%, 1.017, 1.077, 1.121, 1.149 24.3%, 1.159, 1.163, 1.161, 1.154 29.9%, 1.129 32.8%, 1.051 39.6%, 1.017 43.1%, 0.991, 0.977 51%, 0.974 53.8%, 0.975 57.1%, 0.997 69.8%, 1.003 76.9%, 1);
- --ease-spring-4: linear(0, 0.009, 0.037 1.7%, 0.153 3.6%, 0.776 10.3%, 1.001, 1.142 16%, 1.185, 1.209 19%, 1.215 19.9% 20.8%, 1.199, 1.165 25%, 1.056 30.3%, 1.008 33%, 0.973, 0.955 39.2%, 0.953 41.1%, 0.957 43.3%, 0.998 53.3%, 1.009 59.1% 63.7%, 0.998 78.9%, 1);
- --ease-spring-5: linear(0, 0.01, 0.04 1.6%, 0.161 3.3%, 0.816 9.4%, 1.046, 1.189 14.4%, 1.231, 1.254 17%, 1.259, 1.257 18.6%, 1.236, 1.194 22.3%, 1.057 27%, 0.999 29.4%, 0.955 32.1%, 0.942, 0.935 34.9%, 0.933, 0.939 38.4%, 1 47.3%, 1.011, 1.017 52.6%, 1.016 56.4%, 1 65.2%, 0.996 70.2%, 1.001 87.2%, 1);
- --ease-bounce-1: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141, 0.191, 0.25, 0.316, 0.391 36.8%, 0.563, 0.766, 1 58.8%, 0.946, 0.908 69.1%, 0.895, 0.885, 0.879, 0.878, 0.879, 0.885, 0.895, 0.908 89.7%, 0.946, 1);
- --ease-bounce-2: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 15.1%, 0.25, 0.391, 0.562, 0.765, 1, 0.892 45.2%, 0.849, 0.815, 0.788, 0.769, 0.757, 0.753, 0.757, 0.769, 0.788, 0.815, 0.85, 0.892 75.2%, 1 80.2%, 0.973, 0.954, 0.943, 0.939, 0.943, 0.954, 0.973, 1);
- --ease-bounce-3: linear(0, 0.004, 0.016, 0.035, 0.062, 0.098, 0.141 11.4%, 0.25, 0.39, 0.562, 0.764, 1 30.3%, 0.847 34.8%, 0.787, 0.737, 0.699, 0.672, 0.655, 0.65, 0.656, 0.672, 0.699, 0.738, 0.787, 0.847 61.7%, 1 66.2%, 0.946, 0.908, 0.885 74.2%, 0.879, 0.878, 0.879, 0.885 79.5%, 0.908, 0.946, 1 87.4%, 0.981, 0.968, 0.96, 0.957, 0.96, 0.968, 0.981, 1);
- --ease-bounce-4: linear(0, 0.004, 0.016 3%, 0.062, 0.141, 0.25, 0.391, 0.562 18.2%, 1 24.3%, 0.81, 0.676 32.3%, 0.629, 0.595, 0.575, 0.568, 0.575, 0.595, 0.629, 0.676 48.2%, 0.811, 1 56.2%, 0.918, 0.86, 0.825, 0.814, 0.825, 0.86, 0.918, 1 77.2%, 0.94 80.6%, 0.925, 0.92, 0.925, 0.94 87.5%, 1 90.9%, 0.974, 0.965, 0.974, 1);
- --ease-bounce-5: linear(0, 0.004, 0.016 2.5%, 0.063, 0.141, 0.25 10.1%, 0.562, 1 20.2%, 0.783, 0.627, 0.534 30.9%, 0.511, 0.503, 0.511, 0.534 38%, 0.627, 0.782, 1 48.7%, 0.892, 0.815, 0.769 56.3%, 0.757, 0.753, 0.757, 0.769 61.3%, 0.815, 0.892, 1 68.8%, 0.908 72.4%, 0.885, 0.878, 0.885, 0.908 79.4%, 1 83%, 0.954 85.5%, 0.943, 0.939, 0.943, 0.954 90.5%, 1 93%, 0.977, 0.97, 0.977, 1);
- --ease-circ-in: cubic-bezier(.6, .04, .98, .335);
- --ease-circ-in-out: cubic-bezier(.785, .135, .15, .86);
- --ease-circ-out: cubic-bezier(.075, .82, .165, 1);
- --ease-cubic-in: cubic-bezier(.55, .055, .675, .19);
- --ease-cubic-in-out: cubic-bezier(.645, .045, .355, 1);
- --ease-cubic-out: cubic-bezier(.215, .61, .355, 1);
- --ease-expo-in: cubic-bezier(.95, .05, .795, .035);
+ --ease-spring-1: linear(
+ 0,
+ 0.006,
+ 0.025 2.8%,
+ 0.101 6.1%,
+ 0.539 18.9%,
+ 0.721 25.3%,
+ 0.849 31.5%,
+ 0.937 38.1%,
+ 0.968 41.8%,
+ 0.991 45.7%,
+ 1.006 50.1%,
+ 1.015 55%,
+ 1.017 63.9%,
+ 1.001
+ );
+ --ease-spring-2: linear(
+ 0,
+ 0.007,
+ 0.029 2.2%,
+ 0.118 4.7%,
+ 0.625 14.4%,
+ 0.826 19%,
+ 0.902,
+ 0.962,
+ 1.008 26.1%,
+ 1.041 28.7%,
+ 1.064 32.1%,
+ 1.07 36%,
+ 1.061 40.5%,
+ 1.015 53.4%,
+ 0.999 61.6%,
+ 0.995 71.2%,
+ 1
+ );
+ --ease-spring-3: linear(
+ 0,
+ 0.009,
+ 0.035 2.1%,
+ 0.141 4.4%,
+ 0.723 12.9%,
+ 0.938 16.7%,
+ 1.017,
+ 1.077,
+ 1.121,
+ 1.149 24.3%,
+ 1.159,
+ 1.163,
+ 1.161,
+ 1.154 29.9%,
+ 1.129 32.8%,
+ 1.051 39.6%,
+ 1.017 43.1%,
+ 0.991,
+ 0.977 51%,
+ 0.974 53.8%,
+ 0.975 57.1%,
+ 0.997 69.8%,
+ 1.003 76.9%,
+ 1
+ );
+ --ease-spring-4: linear(
+ 0,
+ 0.009,
+ 0.037 1.7%,
+ 0.153 3.6%,
+ 0.776 10.3%,
+ 1.001,
+ 1.142 16%,
+ 1.185,
+ 1.209 19%,
+ 1.215 19.9% 20.8%,
+ 1.199,
+ 1.165 25%,
+ 1.056 30.3%,
+ 1.008 33%,
+ 0.973,
+ 0.955 39.2%,
+ 0.953 41.1%,
+ 0.957 43.3%,
+ 0.998 53.3%,
+ 1.009 59.1% 63.7%,
+ 0.998 78.9%,
+ 1
+ );
+ --ease-spring-5: linear(
+ 0,
+ 0.01,
+ 0.04 1.6%,
+ 0.161 3.3%,
+ 0.816 9.4%,
+ 1.046,
+ 1.189 14.4%,
+ 1.231,
+ 1.254 17%,
+ 1.259,
+ 1.257 18.6%,
+ 1.236,
+ 1.194 22.3%,
+ 1.057 27%,
+ 0.999 29.4%,
+ 0.955 32.1%,
+ 0.942,
+ 0.935 34.9%,
+ 0.933,
+ 0.939 38.4%,
+ 1 47.3%,
+ 1.011,
+ 1.017 52.6%,
+ 1.016 56.4%,
+ 1 65.2%,
+ 0.996 70.2%,
+ 1.001 87.2%,
+ 1
+ );
+ --ease-bounce-1: linear(
+ 0,
+ 0.004,
+ 0.016,
+ 0.035,
+ 0.063,
+ 0.098,
+ 0.141,
+ 0.191,
+ 0.25,
+ 0.316,
+ 0.391 36.8%,
+ 0.563,
+ 0.766,
+ 1 58.8%,
+ 0.946,
+ 0.908 69.1%,
+ 0.895,
+ 0.885,
+ 0.879,
+ 0.878,
+ 0.879,
+ 0.885,
+ 0.895,
+ 0.908 89.7%,
+ 0.946,
+ 1
+ );
+ --ease-bounce-2: linear(
+ 0,
+ 0.004,
+ 0.016,
+ 0.035,
+ 0.063,
+ 0.098,
+ 0.141 15.1%,
+ 0.25,
+ 0.391,
+ 0.562,
+ 0.765,
+ 1,
+ 0.892 45.2%,
+ 0.849,
+ 0.815,
+ 0.788,
+ 0.769,
+ 0.757,
+ 0.753,
+ 0.757,
+ 0.769,
+ 0.788,
+ 0.815,
+ 0.85,
+ 0.892 75.2%,
+ 1 80.2%,
+ 0.973,
+ 0.954,
+ 0.943,
+ 0.939,
+ 0.943,
+ 0.954,
+ 0.973,
+ 1
+ );
+ --ease-bounce-3: linear(
+ 0,
+ 0.004,
+ 0.016,
+ 0.035,
+ 0.062,
+ 0.098,
+ 0.141 11.4%,
+ 0.25,
+ 0.39,
+ 0.562,
+ 0.764,
+ 1 30.3%,
+ 0.847 34.8%,
+ 0.787,
+ 0.737,
+ 0.699,
+ 0.672,
+ 0.655,
+ 0.65,
+ 0.656,
+ 0.672,
+ 0.699,
+ 0.738,
+ 0.787,
+ 0.847 61.7%,
+ 1 66.2%,
+ 0.946,
+ 0.908,
+ 0.885 74.2%,
+ 0.879,
+ 0.878,
+ 0.879,
+ 0.885 79.5%,
+ 0.908,
+ 0.946,
+ 1 87.4%,
+ 0.981,
+ 0.968,
+ 0.96,
+ 0.957,
+ 0.96,
+ 0.968,
+ 0.981,
+ 1
+ );
+ --ease-bounce-4: linear(
+ 0,
+ 0.004,
+ 0.016 3%,
+ 0.062,
+ 0.141,
+ 0.25,
+ 0.391,
+ 0.562 18.2%,
+ 1 24.3%,
+ 0.81,
+ 0.676 32.3%,
+ 0.629,
+ 0.595,
+ 0.575,
+ 0.568,
+ 0.575,
+ 0.595,
+ 0.629,
+ 0.676 48.2%,
+ 0.811,
+ 1 56.2%,
+ 0.918,
+ 0.86,
+ 0.825,
+ 0.814,
+ 0.825,
+ 0.86,
+ 0.918,
+ 1 77.2%,
+ 0.94 80.6%,
+ 0.925,
+ 0.92,
+ 0.925,
+ 0.94 87.5%,
+ 1 90.9%,
+ 0.974,
+ 0.965,
+ 0.974,
+ 1
+ );
+ --ease-bounce-5: linear(
+ 0,
+ 0.004,
+ 0.016 2.5%,
+ 0.063,
+ 0.141,
+ 0.25 10.1%,
+ 0.562,
+ 1 20.2%,
+ 0.783,
+ 0.627,
+ 0.534 30.9%,
+ 0.511,
+ 0.503,
+ 0.511,
+ 0.534 38%,
+ 0.627,
+ 0.782,
+ 1 48.7%,
+ 0.892,
+ 0.815,
+ 0.769 56.3%,
+ 0.757,
+ 0.753,
+ 0.757,
+ 0.769 61.3%,
+ 0.815,
+ 0.892,
+ 1 68.8%,
+ 0.908 72.4%,
+ 0.885,
+ 0.878,
+ 0.885,
+ 0.908 79.4%,
+ 1 83%,
+ 0.954 85.5%,
+ 0.943,
+ 0.939,
+ 0.943,
+ 0.954 90.5%,
+ 1 93%,
+ 0.977,
+ 0.97,
+ 0.977,
+ 1
+ );
+ --ease-circ-in: cubic-bezier(0.6, 0.04, 0.98, 0.335);
+ --ease-circ-in-out: cubic-bezier(0.785, 0.135, 0.15, 0.86);
+ --ease-circ-out: cubic-bezier(0.075, 0.82, 0.165, 1);
+ --ease-cubic-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);
+ --ease-cubic-in-out: cubic-bezier(0.645, 0.045, 0.355, 1);
+ --ease-cubic-out: cubic-bezier(0.215, 0.61, 0.355, 1);
+ --ease-expo-in: cubic-bezier(0.95, 0.05, 0.795, 0.035);
--ease-expo-in-out: cubic-bezier(1, 0, 0, 1);
- --ease-expo-out: cubic-bezier(.19, 1, .22, 1);
- --ease-quad-in: cubic-bezier(.55, .085, .68, .53);
- --ease-quad-in-out: cubic-bezier(.455, .03, .515, .955);
- --ease-quad-out: cubic-bezier(.25, .46, .45, .94);
- --ease-quart-in: cubic-bezier(.895, .03, .685, .22);
- --ease-quart-in-out: cubic-bezier(.77, 0, .175, 1);
- --ease-quart-out: cubic-bezier(.165, .84, .44, 1);
- --ease-quint-in: cubic-bezier(.755, .05, .855, .06);
- --ease-quint-in-out: cubic-bezier(.86, 0, .07, 1);
- --ease-quint-out: cubic-bezier(.23, 1, .32, 1);
- --ease-sine-in: cubic-bezier(.47, 0, .745, .715);
- --ease-sine-in-out: cubic-bezier(.445, .05, .55, .95);
- --ease-sine-out: cubic-bezier(.39, .575, .565, 1);
+ --ease-expo-out: cubic-bezier(0.19, 1, 0.22, 1);
+ --ease-quad-in: cubic-bezier(0.55, 0.085, 0.68, 0.53);
+ --ease-quad-in-out: cubic-bezier(0.455, 0.03, 0.515, 0.955);
+ --ease-quad-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
+ --ease-quart-in: cubic-bezier(0.895, 0.03, 0.685, 0.22);
+ --ease-quart-in-out: cubic-bezier(0.77, 0, 0.175, 1);
+ --ease-quart-out: cubic-bezier(0.165, 0.84, 0.44, 1);
+ --ease-quint-in: cubic-bezier(0.755, 0.05, 0.855, 0.06);
+ --ease-quint-in-out: cubic-bezier(0.86, 0, 0.07, 1);
+ --ease-quint-out: cubic-bezier(0.23, 1, 0.32, 1);
+ --ease-sine-in: cubic-bezier(0.47, 0, 0.745, 0.715);
+ --ease-sine-in-out: cubic-bezier(0.445, 0.05, 0.55, 0.95);
+ --ease-sine-out: cubic-bezier(0.39, 0.575, 0.565, 1);
--layer-1: 1;
--layer-2: 2;
--layer-3: 3;
@@ -225,24 +532,51 @@
--shadow-strength-7: calc(var(--shadow-strength) + 6%);
--shadow-strength-8: calc(var(--shadow-strength) + 7%);
--shadow-strength-10: calc(var(--shadow-strength) + 9%);
- --inner-shadow-highlight: inset 0 -.5px 0 0 #fff, inset 0 .5px 0 0 rgba(0, 0, 0, .067);
+ --inner-shadow-highlight: inset 0 -0.5px 0 0 #fff, inset 0 0.5px 0 0 rgba(0, 0, 0, 0.067);
--shadow-1: 0 1px 2px -1px hsl(var(--shadow-color)/var(--shadow-strength-10));
- --shadow-2: 0 3px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)), 0 7px 14px -5px hsl(var(--shadow-color)/var(--shadow-strength-6));
- --shadow-3: 0 -1px 3px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)), 0 1px 2px -5px hsl(var(--shadow-color)/var(--shadow-strength-3)), 0 2px 5px -5px hsl(var(--shadow-color)/var(--shadow-strength-5)), 0 4px 12px -5px hsl(var(--shadow-color)/var(--shadow-strength-6)), 0 12px 15px -5px hsl(var(--shadow-color)/var(--shadow-strength-8));
- --shadow-4: 0 -2px 5px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)), 0 1px 1px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)), 0 2px 2px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)), 0 5px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-5)), 0 9px 9px -2px hsl(var(--shadow-color)/var(--shadow-strength-6)), 0 16px 16px -2px hsl(var(--shadow-color)/var(--shadow-strength-7));
- --shadow-5: 0 -1px 2px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)), 0 2px 1px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)), 0 5px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)), 0 10px 10px -2px hsl(var(--shadow-color)/var(--shadow-strength-5)), 0 20px 20px -2px hsl(var(--shadow-color)/var(--shadow-strength-6)), 0 40px 40px -2px hsl(var(--shadow-color)/var(--shadow-strength-8));
- --shadow-6: 0 -1px 2px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)), 0 3px 2px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)), 0 7px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)), 0 12px 10px -2px hsl(var(--shadow-color)/var(--shadow-strength-5)), 0 22px 18px -2px hsl(var(--shadow-color)/var(--shadow-strength-6)), 0 41px 33px -2px hsl(var(--shadow-color)/var(--shadow-strength-7)), 0 100px 80px -2px hsl(var(--shadow-color)/var(--shadow-strength-8));
+ --shadow-2:
+ 0 3px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
+ 0 7px 14px -5px hsl(var(--shadow-color)/var(--shadow-strength-6));
+ --shadow-3:
+ 0 -1px 3px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)),
+ 0 1px 2px -5px hsl(var(--shadow-color)/var(--shadow-strength-3)),
+ 0 2px 5px -5px hsl(var(--shadow-color)/var(--shadow-strength-5)),
+ 0 4px 12px -5px hsl(var(--shadow-color)/var(--shadow-strength-6)),
+ 0 12px 15px -5px hsl(var(--shadow-color)/var(--shadow-strength-8));
+ --shadow-4:
+ 0 -2px 5px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)),
+ 0 1px 1px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
+ 0 2px 2px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
+ 0 5px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-5)),
+ 0 9px 9px -2px hsl(var(--shadow-color)/var(--shadow-strength-6)),
+ 0 16px 16px -2px hsl(var(--shadow-color)/var(--shadow-strength-7));
+ --shadow-5:
+ 0 -1px 2px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)),
+ 0 2px 1px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
+ 0 5px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
+ 0 10px 10px -2px hsl(var(--shadow-color)/var(--shadow-strength-5)),
+ 0 20px 20px -2px hsl(var(--shadow-color)/var(--shadow-strength-6)),
+ 0 40px 40px -2px hsl(var(--shadow-color)/var(--shadow-strength-8));
+ --shadow-6:
+ 0 -1px 2px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)),
+ 0 3px 2px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
+ 0 7px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
+ 0 12px 10px -2px hsl(var(--shadow-color)/var(--shadow-strength-5)),
+ 0 22px 18px -2px hsl(var(--shadow-color)/var(--shadow-strength-6)),
+ 0 41px 33px -2px hsl(var(--shadow-color)/var(--shadow-strength-7)),
+ 0 100px 80px -2px hsl(var(--shadow-color)/var(--shadow-strength-8));
--inner-shadow-0: inset 0 0 0 1px hsl(var(--shadow-color)/var(--shadow-strength-10));
--inner-shadow-1: inset 0 1px 2px 0 hsl(var(--shadow-color)/var(--shadow-strength-10)), var(--inner-shadow-highlight);
--inner-shadow-2: inset 0 1px 4px 0 hsl(var(--shadow-color)/var(--shadow-strength-10)), var(--inner-shadow-highlight);
--inner-shadow-3: inset 0 2px 8px 0 hsl(var(--shadow-color)/var(--shadow-strength-10)), var(--inner-shadow-highlight);
- --inner-shadow-4: inset 0 2px 14px 0 hsl(var(--shadow-color)/var(--shadow-strength-10)), var(--inner-shadow-highlight);
+ --inner-shadow-4:
+ inset 0 2px 14px 0 hsl(var(--shadow-color)/var(--shadow-strength-10)), var(--inner-shadow-highlight);
--ratio-square: 1;
--ratio-landscape: 4/3;
--ratio-portrait: 3/4;
--ratio-widescreen: 16/9;
--ratio-ultrawide: 18/5;
- --ratio-golden: 1.6180/1;
+ --ratio-golden: 1.618/1;
--gray-0: #f8f9fa;
--gray-1: #f1f3f5;
--gray-2: #e9ecef;
@@ -491,36 +825,111 @@
--jungle-11: #516605;
--jungle-12: #3d4d04;
--gradient-space: ;
- --gradient-1: linear-gradient(to bottom right var(--gradient-space), #1f005c, #5b0060, #870160, #ac255e, #ca485c, #e16b5c, #f39060, #ffb56b);
+ --gradient-1: linear-gradient(
+ to bottom right var(--gradient-space),
+ #1f005c,
+ #5b0060,
+ #870160,
+ #ac255e,
+ #ca485c,
+ #e16b5c,
+ #f39060,
+ #ffb56b
+ );
--gradient-2: linear-gradient(to bottom right var(--gradient-space), #48005c, #8300e2, #a269ff);
- --gradient-3: radial-gradient(circle at top right var(--gradient-space), #0ff, rgba(0, 255, 255, 0)), radial-gradient(circle at bottom left var(--gradient-space), #ff1492, rgba(255, 20, 146, 0));
+ --gradient-3:
+ radial-gradient(circle at top right var(--gradient-space), #0ff, rgba(0, 255, 255, 0)),
+ radial-gradient(circle at bottom left var(--gradient-space), #ff1492, rgba(255, 20, 146, 0));
--gradient-4: linear-gradient(to bottom right var(--gradient-space), #00f5a0, #00d9f5);
--gradient-5: conic-gradient(from -270deg at 75% 110% var(--gradient-space), #f0f, #fffaf0);
--gradient-6: conic-gradient(from -90deg at top left var(--gradient-space), #000, #fff);
--gradient-7: linear-gradient(to bottom right var(--gradient-space), #72c6ef, #004e8f);
--gradient-8: conic-gradient(from 90deg at 50% 0% var(--gradient-space), #111, 50%, #222, #111);
- --gradient-9: conic-gradient(from .5turn at bottom center var(--gradient-space), #add8e6, #fff);
- --gradient-10: conic-gradient(from 90deg at 40% -25% var(--gradient-space), gold, #f79d03, #ee6907, #e6390a, #de0d0d, #d61039, #cf1261, #c71585, #cf1261, #d61039, #de0d0d, #ee6907, #f79d03, gold, gold, gold);
+ --gradient-9: conic-gradient(from 0.5turn at bottom center var(--gradient-space), #add8e6, #fff);
+ --gradient-10: conic-gradient(
+ from 90deg at 40% -25% var(--gradient-space),
+ gold,
+ #f79d03,
+ #ee6907,
+ #e6390a,
+ #de0d0d,
+ #d61039,
+ #cf1261,
+ #c71585,
+ #cf1261,
+ #d61039,
+ #de0d0d,
+ #ee6907,
+ #f79d03,
+ gold,
+ gold,
+ gold
+ );
--gradient-11: conic-gradient(at bottom left var(--gradient-space), #ff1493, cyan);
- --gradient-12: conic-gradient(from 90deg at 25% -10% var(--gradient-space), #ff4500, #d3f340, #7bee85, #afeeee, #7bee85);
- --gradient-13: radial-gradient(circle at 50% 200% var(--gradient-space), #000142, #3b0083, #b300c3, #ff059f, #ff4661, #ffad86, #fff3c7);
+ --gradient-12: conic-gradient(
+ from 90deg at 25% -10% var(--gradient-space),
+ #ff4500,
+ #d3f340,
+ #7bee85,
+ #afeeee,
+ #7bee85
+ );
+ --gradient-13: radial-gradient(
+ circle at 50% 200% var(--gradient-space),
+ #000142,
+ #3b0083,
+ #b300c3,
+ #ff059f,
+ #ff4661,
+ #ffad86,
+ #fff3c7
+ );
--gradient-14: conic-gradient(at top right var(--gradient-space), lime, cyan);
--gradient-15: linear-gradient(to bottom right var(--gradient-space), #c7d2fe, #fecaca, #fef3c7);
--gradient-16: radial-gradient(circle at 50% -250% var(--gradient-space), #374151, #111827, #000);
--gradient-17: conic-gradient(from -90deg at 50% -25% var(--gradient-space), blue, #8a2be2);
- --gradient-18: linear-gradient(0deg var(--gradient-space), rgba(255, 0, 0, .8), rgba(255, 0, 0, 0) 75%), linear-gradient(60deg var(--gradient-space), rgba(255, 255, 0, .8), rgba(255, 255, 0, 0) 75%), linear-gradient(120deg var(--gradient-space), rgba(0, 255, 0, .8), rgba(0, 255, 0, 0) 75%), linear-gradient(180deg var(--gradient-space), rgba(0, 255, 255, .8), rgba(0, 255, 255, 0) 75%), linear-gradient(240deg var(--gradient-space), rgba(0, 0, 255, .8), rgba(0, 0, 255, 0) 75%), linear-gradient(300deg var(--gradient-space), rgba(255, 0, 255, .8), rgba(255, 0, 255, 0) 75%);
+ --gradient-18:
+ linear-gradient(0deg var(--gradient-space), rgba(255, 0, 0, 0.8), rgba(255, 0, 0, 0) 75%),
+ linear-gradient(60deg var(--gradient-space), rgba(255, 255, 0, 0.8), rgba(255, 255, 0, 0) 75%),
+ linear-gradient(120deg var(--gradient-space), rgba(0, 255, 0, 0.8), rgba(0, 255, 0, 0) 75%),
+ linear-gradient(180deg var(--gradient-space), rgba(0, 255, 255, 0.8), rgba(0, 255, 255, 0) 75%),
+ linear-gradient(240deg var(--gradient-space), rgba(0, 0, 255, 0.8), rgba(0, 0, 255, 0) 75%),
+ linear-gradient(300deg var(--gradient-space), rgba(255, 0, 255, 0.8), rgba(255, 0, 255, 0) 75%);
--gradient-19: linear-gradient(to bottom right var(--gradient-space), #ffe259, #ffa751);
- --gradient-20: conic-gradient(from -135deg at -10% center var(--gradient-space), orange, #ff7715, #ff522a, #ff3f47, #ff5482, #ff69b4);
- --gradient-21: conic-gradient(from -90deg at 25% 115% var(--gradient-space), red, #f06, #f0c, #c0f, #60f, #00f, #00f, #00f, #00f);
+ --gradient-20: conic-gradient(
+ from -135deg at -10% center var(--gradient-space),
+ orange,
+ #ff7715,
+ #ff522a,
+ #ff3f47,
+ #ff5482,
+ #ff69b4
+ );
+ --gradient-21: conic-gradient(
+ from -90deg at 25% 115% var(--gradient-space),
+ red,
+ #f06,
+ #f0c,
+ #c0f,
+ #60f,
+ #00f,
+ #00f,
+ #00f,
+ #00f
+ );
--gradient-22: linear-gradient(to bottom right var(--gradient-space), #acb6e5, #86fde8);
--gradient-23: linear-gradient(to bottom right var(--gradient-space), #536976, #292e49);
- --gradient-24: conic-gradient(from .5turn at 0% 0% var(--gradient-space), #00c476, 10%, #82b0ff, 90%, #00c476);
+ --gradient-24: conic-gradient(from 0.5turn at 0% 0% var(--gradient-space), #00c476, 10%, #82b0ff, 90%, #00c476);
--gradient-25: conic-gradient(at 125% 50% var(--gradient-space), #b78cf7, #ff7c94, #ffcf0d, #ff7c94, #b78cf7);
--gradient-26: linear-gradient(to bottom right var(--gradient-space), #9796f0, #fbc7d4);
- --gradient-27: conic-gradient(from .5turn at bottom left var(--gradient-space), #ff1493, #639);
+ --gradient-27: conic-gradient(from 0.5turn at bottom left var(--gradient-space), #ff1493, #639);
--gradient-28: conic-gradient(from -90deg at 50% 105% var(--gradient-space), #fff, orchid);
- --gradient-29: radial-gradient(circle at top right var(--gradient-space), #bfb3ff, rgba(191, 179, 255, 0)), radial-gradient(circle at bottom left var(--gradient-space), #86acf9, rgba(134, 172, 249, 0));
- --gradient-30: radial-gradient(circle at top right var(--gradient-space), #00ff80, rgba(0, 255, 128, 0)), radial-gradient(circle at bottom left var(--gradient-space), #adffd6, rgba(173, 255, 214, 0));
+ --gradient-29:
+ radial-gradient(circle at top right var(--gradient-space), #bfb3ff, rgba(191, 179, 255, 0)),
+ radial-gradient(circle at bottom left var(--gradient-space), #86acf9, rgba(134, 172, 249, 0));
+ --gradient-30:
+ radial-gradient(circle at top right var(--gradient-space), #00ff80, rgba(0, 255, 128, 0)),
+ radial-gradient(circle at bottom left var(--gradient-space), #adffd6, rgba(173, 255, 214, 0));
--noise-1: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.005' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");
--noise-2: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 300 300' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.05' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");
--noise-3: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.25' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");
@@ -531,22 +940,22 @@
--noise-filter-3: contrast(200%) brightness(250%);
--noise-filter-4: contrast(200%) brightness(500%);
--noise-filter-5: contrast(200%) brightness(1000%);
- --animation-fade-in: fade-in .5s var(--ease-3);
+ --animation-fade-in: fade-in 0.5s var(--ease-3);
--animation-fade-in-bloom: fade-in-bloom 2s var(--ease-3);
- --animation-fade-out: fade-out .5s var(--ease-3);
+ --animation-fade-out: fade-out 0.5s var(--ease-3);
--animation-fade-out-bloom: fade-out-bloom 2s var(--ease-3);
- --animation-scale-up: scale-up .5s var(--ease-3);
- --animation-scale-down: scale-down .5s var(--ease-3);
- --animation-slide-out-up: slide-out-up .5s var(--ease-3);
- --animation-slide-out-down: slide-out-down .5s var(--ease-3);
- --animation-slide-out-right: slide-out-right .5s var(--ease-3);
- --animation-slide-out-left: slide-out-left .5s var(--ease-3);
- --animation-slide-in-up: slide-in-up .5s var(--ease-3);
- --animation-slide-in-down: slide-in-down .5s var(--ease-3);
- --animation-slide-in-right: slide-in-right .5s var(--ease-3);
- --animation-slide-in-left: slide-in-left .5s var(--ease-3);
- --animation-shake-x: shake-x .75s var(--ease-out-5);
- --animation-shake-y: shake-y .75s var(--ease-out-5);
+ --animation-scale-up: scale-up 0.5s var(--ease-3);
+ --animation-scale-down: scale-down 0.5s var(--ease-3);
+ --animation-slide-out-up: slide-out-up 0.5s var(--ease-3);
+ --animation-slide-out-down: slide-out-down 0.5s var(--ease-3);
+ --animation-slide-out-right: slide-out-right 0.5s var(--ease-3);
+ --animation-slide-out-left: slide-out-left 0.5s var(--ease-3);
+ --animation-slide-in-up: slide-in-up 0.5s var(--ease-3);
+ --animation-slide-in-down: slide-in-down 0.5s var(--ease-3);
+ --animation-slide-in-right: slide-in-right 0.5s var(--ease-3);
+ --animation-slide-in-left: slide-in-left 0.5s var(--ease-3);
+ --animation-shake-x: shake-x 0.75s var(--ease-out-5);
+ --animation-shake-y: shake-y 0.75s var(--ease-out-5);
--animation-shake-z: shake-z 1s var(--ease-in-out-3);
--animation-spin: spin 2s linear infinite;
--animation-ping: ping 5s var(--ease-out-3) infinite;
@@ -586,305 +995,312 @@
--palette-hue: 250;
--palette-hue-rotate-by: 0;
--palette-chroma: 0.15;
- --color-1: oklch(98% calc(var(--palette-chroma)*0.03) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*0));
- --color-2: oklch(97% calc(var(--palette-chroma)*0.06) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*1));
- --color-3: oklch(93% calc(var(--palette-chroma)*0.1) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*2));
- --color-4: oklch(84% calc(var(--palette-chroma)*0.12) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*3));
- --color-5: oklch(80% calc(var(--palette-chroma)*0.16) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*4));
- --color-6: oklch(71% calc(var(--palette-chroma)*0.19) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*5));
- --color-7: oklch(66% calc(var(--palette-chroma)*0.2) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*6));
- --color-8: oklch(58% calc(var(--palette-chroma)*0.21) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*7));
- --color-9: oklch(53% calc(var(--palette-chroma)*0.2) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*8));
- --color-10: oklch(49% calc(var(--palette-chroma)*0.19) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*9));
- --color-11: oklch(42% calc(var(--palette-chroma)*0.17) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*10));
- --color-12: oklch(35% calc(var(--palette-chroma)*0.15) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*11));
- --color-13: oklch(27% calc(var(--palette-chroma)*0.12) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*12));
- --color-14: oklch(20% calc(var(--palette-chroma)*0.09) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*13));
- --color-15: oklch(16% calc(var(--palette-chroma)*0.07) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*14));
- --color-16: oklch(10% calc(var(--palette-chroma)*0.05) calc(var(--palette-hue) + var(--palette-hue-rotate-by)*15))
+ --color-1: oklch(98% calc(var(--palette-chroma) * 0.03) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 0));
+ --color-2: oklch(97% calc(var(--palette-chroma) * 0.06) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 1));
+ --color-3: oklch(93% calc(var(--palette-chroma) * 0.1) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 2));
+ --color-4: oklch(84% calc(var(--palette-chroma) * 0.12) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 3));
+ --color-5: oklch(80% calc(var(--palette-chroma) * 0.16) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 4));
+ --color-6: oklch(71% calc(var(--palette-chroma) * 0.19) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 5));
+ --color-7: oklch(66% calc(var(--palette-chroma) * 0.2) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 6));
+ --color-8: oklch(58% calc(var(--palette-chroma) * 0.21) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 7));
+ --color-9: oklch(53% calc(var(--palette-chroma) * 0.2) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 8));
+ --color-10: oklch(49% calc(var(--palette-chroma) * 0.19) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 9));
+ --color-11: oklch(
+ 42% calc(var(--palette-chroma) * 0.17) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 10)
+ );
+ --color-12: oklch(
+ 35% calc(var(--palette-chroma) * 0.15) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 11)
+ );
+ --color-13: oklch(
+ 27% calc(var(--palette-chroma) * 0.12) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 12)
+ );
+ --color-14: oklch(
+ 20% calc(var(--palette-chroma) * 0.09) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 13)
+ );
+ --color-15: oklch(
+ 16% calc(var(--palette-chroma) * 0.07) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 14)
+ );
+ --color-16: oklch(
+ 10% calc(var(--palette-chroma) * 0.05) calc(var(--palette-hue) + var(--palette-hue-rotate-by) * 15)
+ );
}
-@media (prefers-color-scheme:dark) {
+@media (prefers-color-scheme: dark) {
:where(html) {
--shadow-color: 220 40% 2%;
--shadow-strength: 25%;
- --inner-shadow-highlight: inset 0 -.5px 0 0 hsla(0, 0%, 100%, .067), inset 0 .5px 0 0 rgba(0, 0, 0, .467)
+ --inner-shadow-highlight: inset 0 -0.5px 0 0 hsla(0, 0%, 100%, 0.067), inset 0 0.5px 0 0 rgba(0, 0, 0, 0.467);
}
}
-@supports (background:linear-gradient(to right in oklab, #000, #fff)) {
+@supports (background: linear-gradient(to right in oklab, #000, #fff)) {
:where(html) {
- --gradient-space: in oklab
+ --gradient-space: in oklab;
}
}
@keyframes fade-in {
to {
- opacity: 1
+ opacity: 1;
}
}
@keyframes fade-in-bloom {
0% {
filter: brightness(1) blur(20px);
- opacity: 0
+ opacity: 0;
}
10% {
filter: brightness(2) blur(10px);
- opacity: 1
+ opacity: 1;
}
to {
filter: brightness(1) blur(0);
- opacity: 1
+ opacity: 1;
}
}
@keyframes fade-out {
to {
- opacity: 0
+ opacity: 0;
}
}
@keyframes fade-out-bloom {
to {
filter: brightness(1) blur(20px);
- opacity: 0
+ opacity: 0;
}
10% {
filter: brightness(2) blur(10px);
- opacity: 1
+ opacity: 1;
}
0% {
filter: brightness(1) blur(0);
- opacity: 1
+ opacity: 1;
}
}
@keyframes scale-up {
to {
- transform: scale(1.25)
+ transform: scale(1.25);
}
}
@keyframes scale-down {
to {
- transform: scale(.75)
+ transform: scale(0.75);
}
}
@keyframes slide-out-up {
to {
- transform: translateY(-100%)
+ transform: translateY(-100%);
}
}
@keyframes slide-out-down {
to {
- transform: translateY(100%)
+ transform: translateY(100%);
}
}
@keyframes slide-out-right {
to {
- transform: translateX(100%)
+ transform: translateX(100%);
}
}
@keyframes slide-out-left {
to {
- transform: translateX(-100%)
+ transform: translateX(-100%);
}
}
@keyframes slide-in-up {
0% {
- transform: translateY(100%)
+ transform: translateY(100%);
}
}
@keyframes slide-in-down {
0% {
- transform: translateY(-100%)
+ transform: translateY(-100%);
}
}
@keyframes slide-in-right {
0% {
- transform: translateX(-100%)
+ transform: translateX(-100%);
}
}
@keyframes slide-in-left {
0% {
- transform: translateX(100%)
+ transform: translateX(100%);
}
}
@keyframes shake-x {
-
0%,
to {
- transform: translateX(0)
+ transform: translateX(0);
}
20% {
- transform: translateX(-5%)
+ transform: translateX(-5%);
}
40% {
- transform: translateX(5%)
+ transform: translateX(5%);
}
60% {
- transform: translateX(-5%)
+ transform: translateX(-5%);
}
80% {
- transform: translateX(5%)
+ transform: translateX(5%);
}
}
@keyframes shake-y {
-
0%,
to {
- transform: translateY(0)
+ transform: translateY(0);
}
20% {
- transform: translateY(-5%)
+ transform: translateY(-5%);
}
40% {
- transform: translateY(5%)
+ transform: translateY(5%);
}
60% {
- transform: translateY(-5%)
+ transform: translateY(-5%);
}
80% {
- transform: translateY(5%)
+ transform: translateY(5%);
}
}
@keyframes shake-z {
-
0%,
to {
- transform: rotate(0deg)
+ transform: rotate(0deg);
}
20% {
- transform: rotate(-2deg)
+ transform: rotate(-2deg);
}
40% {
- transform: rotate(2deg)
+ transform: rotate(2deg);
}
60% {
- transform: rotate(-2deg)
+ transform: rotate(-2deg);
}
80% {
- transform: rotate(2deg)
+ transform: rotate(2deg);
}
}
@keyframes spin {
to {
- transform: rotate(1turn)
+ transform: rotate(1turn);
}
}
@keyframes ping {
-
90%,
to {
opacity: 0;
- transform: scale(2)
+ transform: scale(2);
}
}
@keyframes blink {
-
0%,
to {
- opacity: 1
+ opacity: 1;
}
50% {
- opacity: .5
+ opacity: 0.5;
}
}
@keyframes float {
50% {
- transform: translateY(-25%)
+ transform: translateY(-25%);
}
}
@keyframes bounce {
25% {
- transform: translateY(-20%)
+ transform: translateY(-20%);
}
40% {
- transform: translateY(-3%)
+ transform: translateY(-3%);
}
0%,
60%,
to {
- transform: translateY(0)
+ transform: translateY(0);
}
}
@keyframes pulse {
50% {
- transform: scale(.9)
+ transform: scale(0.9);
}
}
-@media (prefers-color-scheme:dark) {
+@media (prefers-color-scheme: dark) {
@keyframes fade-in-bloom {
0% {
filter: brightness(1) blur(20px);
- opacity: 0
+ opacity: 0;
}
10% {
- filter: brightness(.5) blur(10px);
- opacity: 1
+ filter: brightness(0.5) blur(10px);
+ opacity: 1;
}
to {
filter: brightness(1) blur(0);
- opacity: 1
+ opacity: 1;
}
}
}
-@media (prefers-color-scheme:dark) {
+@media (prefers-color-scheme: dark) {
@keyframes fade-out-bloom {
to {
filter: brightness(1) blur(20px);
- opacity: 0
+ opacity: 0;
}
10% {
- filter: brightness(.5) blur(10px);
- opacity: 1
+ filter: brightness(0.5) blur(10px);
+ opacity: 1;
}
0% {
filter: brightness(1) blur(0);
- opacity: 1
+ opacity: 1;
}
}
}
diff --git a/src/app/styles/_typography.scss b/src/app/styles/_typography.scss
index e53c30f..523a21a 100644
--- a/src/app/styles/_typography.scss
+++ b/src/app/styles/_typography.scss
@@ -1,5 +1,4 @@
@layer typography {
-
h1,
h2,
h3,
diff --git a/src/app/styles/components/_button.scss b/src/app/styles/components/_button.scss
index 4ce20e4..fa3e123 100644
--- a/src/app/styles/components/_button.scss
+++ b/src/app/styles/components/_button.scss
@@ -14,6 +14,10 @@
font-size: var(--font-size);
font-weight: var(--font-weight);
transition: 0.2s;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-xxs);
&:not(&:disabled):hover {
background-color: var(--btn-bg-color-hover);
diff --git a/src/app/styles/components/_code.scss b/src/app/styles/components/_code.scss
new file mode 100644
index 0000000..b94cc62
--- /dev/null
+++ b/src/app/styles/components/_code.scss
@@ -0,0 +1,29 @@
+@layer components {
+ .code {
+ position: relative;
+ border: 1px solid var(--color-bg-disabled);
+ background-color: var(--color-bg-secondary);
+ padding: var(--space-s);
+ border-radius: var(--border-radius);
+
+ .copy {
+ position: absolute;
+ top: var(--space-xs);
+ right: var(--space-xs);
+
+ .feedback {
+ position: absolute;
+ top: 0;
+ right: 0;
+ transform: translateY(-110%);
+ background-color: var(--color-bg-secondary);
+ color: var(--color-fg-secondary);
+ border: 1px solid var(--color-fg-secondary);
+ border-radius: var(--border-radius);
+ padding: var(--space-xxs);
+ white-space: nowrap;
+ transition: 0.2s;
+ }
+ }
+ }
+}
diff --git a/src/app/styles/components/_icon.scss b/src/app/styles/components/_icon.scss
index 3deb5c6..9cc12eb 100644
--- a/src/app/styles/components/_icon.scss
+++ b/src/app/styles/components/_icon.scss
@@ -2,11 +2,21 @@
.icon {
width: var(--size-xs);
height: var(--size-xs);
- display: flex;
+ display: inline-flex;
align-items: center;
justify-content: center;
color: inherit;
+ &.size-xxs {
+ width: var(--size-xxs);
+ height: var(--size-xxs);
+ }
+
+ &.size-xs {
+ width: var(--size-xs);
+ height: var(--size-xs);
+ }
+
&.size-s {
width: var(--size-s);
height: var(--size-s);
diff --git a/src/app/styles/components/_index.scss b/src/app/styles/components/_index.scss
index c6f6aba..eadae6c 100644
--- a/src/app/styles/components/_index.scss
+++ b/src/app/styles/components/_index.scss
@@ -6,3 +6,4 @@
@forward "./icon";
@forward "./switch-section";
@forward "./input";
+@forward "./code";
diff --git a/src/app/styles/utilities/_display.scss b/src/app/styles/utilities/_display.scss
new file mode 100644
index 0000000..1ed160f
--- /dev/null
+++ b/src/app/styles/utilities/_display.scss
@@ -0,0 +1,5 @@
+@layer utilities {
+ .display-none {
+ display: none;
+ }
+}
diff --git a/src/app/styles/utilities/_index.scss b/src/app/styles/utilities/_index.scss
index bb4b9be..3bbb43f 100644
--- a/src/app/styles/utilities/_index.scss
+++ b/src/app/styles/utilities/_index.scss
@@ -2,3 +2,4 @@
@forward "./position";
@forward "./grid";
@forward "./sizes";
+@forward "./display";
diff --git a/src/app/users-generator/page.tsx b/src/app/users-generator/page.tsx
index 825d4f1..98427c3 100644
--- a/src/app/users-generator/page.tsx
+++ b/src/app/users-generator/page.tsx
@@ -1,13 +1,15 @@
"use client";
import { UsersGeneratorContext } from "./template";
-import { useContext, useMemo } from "react";
+import { useContext, useMemo, useState } from "react";
import SwitchSection from "@/components/switch-section";
-import { useFetch } from "@/hooks/useFetch";
+import { useFetchUsers } from "@/hooks/useFetchUsers";
import { Button } from "@/components/ui";
import * as Prism from "prismjs";
import "prismjs/components/prism-json";
import "prismjs/components/prism-sql";
import buildQuery from "@/utilities/buildQuery";
+import Icon from "@/components/ui/icon";
+import { cn } from "@/utilities";
export default function UsersGeneratorPage() {
const {
@@ -21,60 +23,101 @@ export default function UsersGeneratorPage() {
seed,
} = useContext(UsersGeneratorContext);
- const { isLoading, refetch, data, error } = useFetch();
+ const { isLoading, refetch, data } = useFetchUsers();
+
+ const fields = useMemo(() => {
+ let fields = 0;
+ if (isName) fields |= 1;
+ if (isSurname) fields |= 2;
+ if (isUsername) fields |= 4;
+ if (isAvatar) fields |= 8;
+ if (isEmail) fields |= 16;
+ if (isPassword) fields |= 32;
+ return fields;
+ }, [isName, isSurname, isUsername, isAvatar, isEmail, isPassword]);
+
+ const jsonText = useMemo(() => {
+ const jsonString = JSON.stringify(data?.ok ? data.value : [], null, 2);
+ return jsonString;
+ }, [data]);
+ const sqlText = useMemo(() => {
+ const sqlString = data?.ok ? buildQuery(data) : "";
+ return sqlString;
+ }, [data]);
+ const [isCopied, setIsCopied] = useState(false);
const highlightedJson = useMemo(() => {
- const jsonString = JSON.stringify(data?.data ?? [], null, 2);
+ if (!data) return "[]";
+ if (!data.ok) return Prism.highlight(data.error, Prism.languages.json, "json");
+ const jsonString = JSON.stringify(data.value, null, 2);
return Prism.highlight(jsonString, Prism.languages.json, "json");
}, [data]);
const highlightedSql = useMemo(() => {
- const sqlString = buildQuery(data?.data ?? []);
+ if (!data) return "";
+ if (!data.ok) return Prism.highlight(data.error, Prism.languages.json, "json");
+ const sqlString = buildQuery(data);
return Prism.highlight(sqlString, Prism.languages.sql, "sql");
}, [data]);
const handleGenerate = () => {
- refetch({
- count, fields: [
- isName ? "name" : "",
- isSurname ? "surname" : "",
- isUsername ? "username" : "",
- isAvatar ? "avatar" : "",
- isEmail ? "email" : "",
- isPassword ? "password" : "",
- ], seed
- })
- }
+ refetch({ count, fields, seed });
+ };
+
+ const copyJson = async () => {
+ try {
+ await navigator.clipboard.writeText(jsonText);
+ setIsCopied(true);
+ setTimeout(() => {
+ setIsCopied(false);
+ }, 2000);
+ } catch {}
+ };
+
+ const copySql = async () => {
+ try {
+ await navigator.clipboard.writeText(sqlText);
+ setIsCopied(true);
+ setTimeout(() => {
+ setIsCopied(false);
+ }, 2000);
+ } catch {}
+ };
return (
<>
Random users generator
- Name: {isName ? "enabled" : "disabled"}
- Surname: {isSurname ? "enabled" : "disabled"}
- Username: {isUsername ? "enabled" : "disabled"}
- Avatar: {isAvatar ? "enabled" : "disabled"}
- Email: {isEmail ? "enabled" : "disabled"}
- Password: {isPassword ? "enabled" : "disabled"}
+ You can choose what gets generated in the settings.
- Generate
+
+ Generate
+
-
-
-
+
+
+
+ Copy
+ Copied to clipboard
+
+
+
+
+
-
-
-
+
+
+
+ Copy
+ Copied to clipboard
+
+
+
+
+
>
diff --git a/src/app/users-generator/template.tsx b/src/app/users-generator/template.tsx
index 4525613..c86a548 100644
--- a/src/app/users-generator/template.tsx
+++ b/src/app/users-generator/template.tsx
@@ -3,9 +3,9 @@ import { ReactNode, createContext, useState } from "react";
import Sidebar from "@/components/sidebar";
import { Toggle, Input } from "@/components/ui";
import { cn } from "@/utilities";
-import { GeneratorSettings } from "@/types/generator";
+import { UserSettings } from "@/types/generator";
-export const UsersGeneratorContext = createContext({
+export const UsersGeneratorContext = createContext({
name: false,
surname: false,
username: false,
@@ -34,12 +34,24 @@ export default function UsersGeneratorTemplate({ children }: { children: ReactNo
-
- {children}
-
+ {children}
>
);
diff --git a/src/components/header.tsx b/src/components/header.tsx
index 18cabd6..739bd67 100644
--- a/src/components/header.tsx
+++ b/src/components/header.tsx
@@ -19,7 +19,12 @@ export default function Header() {
useEffect(() => {
const HideSidebarOnClick = (e: MouseEvent) => {
- if (sidebarRef.current && headerRef.current && !sidebarRef.current.contains(e.target as Node) && !headerRef.current.contains(e.target as Node)) {
+ if (
+ sidebarRef.current &&
+ headerRef.current &&
+ !sidebarRef.current.contains(e.target as Node) &&
+ !headerRef.current.contains(e.target as Node)
+ ) {
setIsSidebarOpen(false);
}
};
@@ -28,26 +33,32 @@ export default function Header() {
return () => {
document.removeEventListener("click", HideSidebarOnClick);
- }
+ };
}, []);
return (
-
Mock data generator
+
+ Mock data generator
+
- users generator
+
+ users generator
+
-
-
users generator
+
+
+ users generator
+
- )
+ );
}
diff --git a/src/components/section.tsx b/src/components/section.tsx
index 1688fe9..0ab0f96 100644
--- a/src/components/section.tsx
+++ b/src/components/section.tsx
@@ -1,13 +1,6 @@
import { ReactNode } from "react";
import { cn } from "@/utilities";
-export default function Section({ children, variant }: {
- children: ReactNode,
- variant?: "secondary" | "disabled"
-}) {
- return (
-
- {children}
-
- )
+export default function Section({ children, variant }: { children: ReactNode; variant?: "secondary" | "disabled" }) {
+ return
{children}
;
}
diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx
index 4cb833e..6be3ac6 100644
--- a/src/components/sidebar.tsx
+++ b/src/components/sidebar.tsx
@@ -1,4 +1,4 @@
-"use client"
+"use client";
import { ReactNode, useState, useEffect, useRef } from "react";
import { cn } from "@/utilities";
import Icon from "./ui/icon";
@@ -14,7 +14,12 @@ export default function Sidebar({ children }: { children: ReactNode }) {
useEffect(() => {
const HideSidebarOnClick = (e: MouseEvent) => {
- if (sidebarRef.current && sidebarToggleRef.current && !sidebarRef.current.contains(e.target as Node) && !sidebarToggleRef.current.contains(e.target as Node)) {
+ if (
+ sidebarRef.current &&
+ sidebarToggleRef.current &&
+ !sidebarRef.current.contains(e.target as Node) &&
+ !sidebarToggleRef.current.contains(e.target as Node)
+ ) {
setIsSidebarOpen(false);
}
};
@@ -23,7 +28,7 @@ export default function Sidebar({ children }: { children: ReactNode }) {
return () => {
document.removeEventListener("click", HideSidebarOnClick);
- }
+ };
}, []);
return (
@@ -36,9 +41,9 @@ export default function Sidebar({ children }: { children: ReactNode }) {
-
+
{children}
>
- )
+ );
}
diff --git a/src/components/switch-section.tsx b/src/components/switch-section.tsx
index 2edf90f..26e6eb7 100644
--- a/src/components/switch-section.tsx
+++ b/src/components/switch-section.tsx
@@ -1,11 +1,8 @@
-"use client"
+"use client";
import { ReactNode, Children, useEffect, useState, isValidElement, createElement } from "react";
import { cn } from "@/utilities";
-export default function SwitchSection({ children, className }: {
- children: ReactNode,
- className?: string
-}) {
+export default function SwitchSection({ children, className }: { children: ReactNode; className?: string }) {
const [childrenArray, setChildrenArray] = useState
([]);
const [activeIndex, setActiveIndex] = useState(0);
@@ -15,7 +12,6 @@ export default function SwitchSection({ children, className }: {
return (
-
{childrenArray.map((el: ReactNode, i: number) => {
if (isValidElement<{ id?: string }>(el)) {
@@ -30,19 +26,19 @@ export default function SwitchSection({ children, className }: {
{childrenArray.map((el: ReactNode, i: number) => {
- if (isValidElement<{ id?: string, children?: ReactNode, className?: string }>(el)) {
+ if (isValidElement<{ id?: string; children?: ReactNode; className?: string }>(el)) {
return createElement(
"div",
{
...el.props,
key: i,
- className: cn(el.props.className, "content-item", { "active": i === activeIndex })
+ className: cn(el.props.className, "content-item", { active: i === activeIndex }),
},
- el.props.children
+ el.props.children,
);
}
})}
- )
+ );
}
diff --git a/src/components/ui/icon.tsx b/src/components/ui/icon.tsx
index 7050963..65aac2f 100644
--- a/src/components/ui/icon.tsx
+++ b/src/components/ui/icon.tsx
@@ -11,6 +11,12 @@ const icons = {
+ ),
+ copy: (
+
+
+
+
)
}
diff --git a/src/hooks/useFetch.ts b/src/hooks/useFetch.ts
deleted file mode 100644
index fc63fb4..0000000
--- a/src/hooks/useFetch.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { useState } from "react";
-import { GeneratorArgs, GeneratorResponse } from "@/types/generator";
-
-export function useFetch() {
- const [isLoading, setIsLoading] = useState
(false);
- const [data, setData] = useState(null);
- const [error, setError] = useState(null);
-
- const refetch = async ({ count, fields, seed }: GeneratorArgs) => {
- setIsLoading(true);
- setError(null);
- try {
- const response = await fetch("/api/generate", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ count, fields, seed }),
- });
-
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
-
- const json = await response.json();
- setData(json);
- } catch (err) {
- setError(err instanceof Error ? err.message : "An error occurred");
- setData(null);
- } finally {
- setIsLoading(false);
- }
- };
-
- return { isLoading, refetch, data, error };
-}
diff --git a/src/hooks/useFetchPosts.ts b/src/hooks/useFetchPosts.ts
new file mode 100644
index 0000000..7ee03e3
--- /dev/null
+++ b/src/hooks/useFetchPosts.ts
@@ -0,0 +1,29 @@
+import { useState } from "react";
+import { PostArgs, GeneratorResponse } from "@/types/generator";
+
+export function useFetchPosts() {
+ const [isLoading, setIsLoading] = useState(false);
+ const [data, setData] = useState(null);
+
+ const refetch = async ({ count, seed }: PostArgs) => {
+ setIsLoading(true);
+ try {
+ const response = await fetch("/api/generator/posts", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ count, seed }),
+ });
+
+ const json = await response.json();
+ setData(json);
+ } catch {
+ setData({ ok: false, error: "Internal server error" });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return { isLoading, refetch, data };
+}
diff --git a/src/hooks/useFetchUsers.ts b/src/hooks/useFetchUsers.ts
new file mode 100644
index 0000000..cbc289f
--- /dev/null
+++ b/src/hooks/useFetchUsers.ts
@@ -0,0 +1,54 @@
+import { useState } from "react";
+import { UserArgs, GeneratorResponse } from "@/types/generator";
+
+export function useFetchUsers() {
+ const [isLoading, setIsLoading] = useState(false);
+ const [data, setData] = useState(null);
+
+ const validate = ({ count, fields, seed }: UserArgs): { ok: boolean; error?: string } => {
+ if (
+ typeof fields !== "number" ||
+ typeof count !== "number" ||
+ (seed !== undefined && typeof seed !== "number" && typeof seed !== "number")
+ ) {
+ return { ok: false, error: "Invalid arguments" };
+ }
+
+ if (fields < 1 || fields > 63) {
+ return { ok: false, error: "No fields selected" };
+ }
+
+ if (count < 1 || count > 300) {
+ return { ok: false, error: "Count must be between 1 and 300" };
+ }
+
+ return { ok: true };
+ };
+
+ const refetch = async ({ count, fields, seed }: UserArgs) => {
+ const validation = validate({ count, fields });
+ if (!validation.ok) {
+ setData({ ok: false, error: validation.error! });
+ return;
+ }
+ setIsLoading(true);
+ try {
+ const response = await fetch("/api/generator/users", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ count, fields, seed }),
+ });
+
+ const json = await response.json();
+ setData(json);
+ } catch {
+ setData({ ok: false, error: "Internal server error" });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return { isLoading, refetch, data };
+}
diff --git a/src/types/generator.ts b/src/types/generator.ts
index ebfffd8..938ccc6 100644
--- a/src/types/generator.ts
+++ b/src/types/generator.ts
@@ -1,19 +1,15 @@
-export interface GeneratorResponse {
- meta: {
- count: number;
- generatedAt: string;
- message: string;
- };
- data: Record[];
+export interface UserArgs {
+ count: number;
+ fields: number | string[];
+ seed?: number;
}
-export interface GeneratorArgs {
+export interface PostArgs {
count: number;
- fields: number | string[];
seed?: number;
}
-export interface GeneratorSettings {
+export interface UserSettings {
name: boolean;
surname: boolean;
username: boolean;
@@ -23,3 +19,15 @@ export interface GeneratorSettings {
count: number;
seed: number | undefined;
}
+
+export interface Ok {
+ ok: true;
+ value: Record[];
+}
+
+export interface Err {
+ ok: false;
+ error: string;
+}
+
+export type GeneratorResponse = Ok | Err;
diff --git a/src/utilities/buildQuery.ts b/src/utilities/buildQuery.ts
index 195a267..9d367d1 100644
--- a/src/utilities/buildQuery.ts
+++ b/src/utilities/buildQuery.ts
@@ -1,11 +1,16 @@
-export default function buildQuery(data: Record[]): string {
- if (data.length === 0) {
- return "";
- }
+import { Ok } from "@/types/generator";
- const columns = Object.keys(data[0]).join(", ");
- const values = data
- .map((row) => `(${Object.values(row).map((value) => `"${value}"`).join(", ")})`)
+export default function buildUserQuery(data: Ok): string {
+ const tableName = data.value[0].hasOwnProperty("authorId") ? "posts" : "users";
+
+ const columns = Object.keys(data.value[0]).join(", ");
+ const values = data.value
+ .map(
+ (row) =>
+ `(${Object.values(row)
+ .map((value) => `"${value}"`)
+ .join(", ")})`,
+ )
.join(",\n");
- return `INSERT INTO users (${columns}) VALUES\n${values};`;
+ return `INSERT INTO ${tableName} (${columns}) VALUES\n${values};`;
}
diff --git a/src/utilities/dataGenerator.ts b/src/utilities/dataGenerator.ts
index 0e73c73..0079b2c 100644
--- a/src/utilities/dataGenerator.ts
+++ b/src/utilities/dataGenerator.ts
@@ -1,10 +1,5 @@
-"use server";
import { faker } from "@faker-js/faker";
-import { GeneratorArgs } from "../types/generator";
-// config start
-const errorSuccess = 0; // "Data generated successfully.";
-const errorInvalidFields = 1; // "Invalid fields selected.";
-const errorInvalidCount = 2; // "Invalid count, must be between 1 and 300.";
+import { UserArgs, GeneratorResponse } from "../types/generator";
const availableFields = 6; // 6 fields: name, surname, username, email, password, avatar
@@ -40,17 +35,14 @@ const generators = [
generator: () => faker.internet.password(),
},
];
-// config end
-export async function generateData({ count, fields, seed }: GeneratorArgs): Promise<{
- result: number;
- data: Record[];
-}> {
- let returnCode = errorSuccess;
-
- if (count > 300 || count < 1) {
- console.error("Count is invalid: ", count);
- returnCode = errorInvalidCount;
+export default async function generateData({ count, fields, seed }: UserArgs): Promise {
+ if (typeof count !== "number" || count > 300 || count < 1) {
+ // console.error("Count is invalid: ", count); // dev
+ return {
+ ok: false,
+ error: "Invalid count, must be a number between 1 and 300.",
+ };
}
if (seed !== undefined) {
@@ -61,8 +53,11 @@ export async function generateData({ count, fields, seed }: GeneratorArgs): Prom
if (typeof fields === "number") {
if (fields >= 2 ** availableFields || fields < 1) {
- console.error("Invalid fields number: ", fields);
- returnCode = errorInvalidFields;
+ // console.error("Invalid fields number: ", fields); // dev
+ return {
+ ok: false,
+ error: "Invalid fields number, must be between 1 and " + (2 ** availableFields - 1),
+ };
}
for (let i = 0; i < count; i++) {
@@ -86,12 +81,15 @@ export async function generateData({ count, fields, seed }: GeneratorArgs): Prom
data.push(record);
}
} else {
- console.error("Invalid fields type: ", fields);
- returnCode = errorInvalidFields;
+ // console.error("Invalid fields type: ", fields); // dev
+ return {
+ ok: false,
+ error: "Invalid fields type, must be a number or an array.",
+ };
}
return {
- result: returnCode,
- data: data,
+ ok: true,
+ value: data,
};
}
diff --git a/src/utilities/postsGenerator.ts b/src/utilities/postsGenerator.ts
new file mode 100644
index 0000000..ea1015e
--- /dev/null
+++ b/src/utilities/postsGenerator.ts
@@ -0,0 +1,30 @@
+import { faker } from "@faker-js/faker";
+import { GeneratorResponse, PostArgs } from "@/types/generator";
+
+export default async function generatePosts({ count, seed }: PostArgs): Promise {
+ if (typeof count !== "number" || count > 300 || count < 1) {
+ return {
+ ok: false,
+ error: "Invalid count, must be a number between 1 and 300.",
+ };
+ }
+
+ if (seed !== undefined) {
+ faker.seed(seed);
+ }
+
+ const data: Record[] = [];
+
+ for (let i = 0; i < count; i++) {
+ const record: Record = {};
+ record["title"] = faker.hacker.phrase();
+ record["content"] = faker.lorem.paragraphs();
+ record["authorId"] = (i + 1).toString();
+ data.push(record);
+ }
+
+ return {
+ ok: true,
+ value: data,
+ };
+}
diff --git a/src/utilities/ratelimit/rateLimiter.ts b/src/utilities/ratelimit/rateLimiter.ts
new file mode 100644
index 0000000..133affd
--- /dev/null
+++ b/src/utilities/ratelimit/rateLimiter.ts
@@ -0,0 +1,35 @@
+import { RateLimiterMemory, RateLimiterRes } from "rate-limiter-flexible";
+import { NextRequest } from "next/server";
+
+const rateLimiter = new RateLimiterMemory({
+ points: 10,
+ duration: 1,
+});
+
+export async function checkRateLimit(request: NextRequest): Promise<{ success: boolean; headers: Headers }> {
+ try {
+ const ip = request.headers.get("x-forwarded-for") ?? "127.0.0.1";
+ const res = await rateLimiter.consume(ip, 1);
+
+ const headers = new Headers({
+ "X-RateLimit-Limit": "10",
+ "X-RateLimit-Remaining": String(res.remainingPoints),
+ "X-RateLimit-Reset": new Date(Date.now() + res.msBeforeNext).toISOString(),
+ });
+
+ return { success: true, headers };
+ } catch (err: unknown) {
+ if (err instanceof Error) {
+ return { success: false, headers: new Headers() };
+ }
+ const rejRes = err as RateLimiterRes;
+ const headers = new Headers({
+ "Retry-After": String(Math.round(rejRes.msBeforeNext / 1000) || 1),
+ "X-RateLimit-Limit": "10",
+ "X-RateLimit-Remaining": String(rejRes.remainingPoints),
+ "X-RateLimit-Reset": new Date(Date.now() + rejRes.msBeforeNext).toISOString(),
+ });
+
+ return { success: false, headers };
+ }
+}