mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
documentation structure
This commit is contained in:
@@ -23,6 +23,7 @@ export default defineConfigWithVueTs(
|
||||
files: ['**/*.vue'],
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/no-reserved-component-names': 'off',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
13
index.html
13
index.html
@@ -1,13 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="theme-color" content="#0e0e0e">
|
||||
<meta name="theme-color" content="#0b0b0b">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Bricolage+Grotesque:opsz,wght@12..96,200..800&family=Figtree:ital,wght@0,300..900;1,300..900&family=Gochi+Hand&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<title>Vue Bits</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
162
package-lock.json
generated
162
package-lock.json
generated
@@ -8,11 +8,15 @@
|
||||
"name": "vue-bits",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@primeuix/themes": "^1.2.1",
|
||||
"@wdns/vue-code-block": "^2.3.5",
|
||||
"gsap": "^3.13.0",
|
||||
"ogl": "^1.0.11",
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "^4.3.6",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-sonner": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
@@ -1307,6 +1311,74 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@primeuix/styled": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.7.0.tgz",
|
||||
"integrity": "sha512-xUqMdQb75izeDkNWFK1QlU15aUl5LIU97Fq68IXOhrqqLsKEBnj5ftntFZrENQW70jAHwALdWP4EOGi/poc9Tg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/utils": "^0.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primeuix/styles": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@primeuix/styles/-/styles-1.2.1.tgz",
|
||||
"integrity": "sha512-Tri7pPgZgxrVmhJG8ijZZFolQ6vu27xnkGoAB9EFY8YlaKTM5iqkWzEcqdxy2KmgFWMXi+BrPHwO0RdQ6JCT+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/styled": "^0.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primeuix/themes": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@primeuix/themes/-/themes-1.2.1.tgz",
|
||||
"integrity": "sha512-DVCFDncvag47tpag3TdufDTuvUbfKnkgzmlxAQkwuMS0IlPA3wChrf9VlL2wETg/ZpJm/tHobkJBnB9FmkiqnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/styled": "^0.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primeuix/utils": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.6.0.tgz",
|
||||
"integrity": "sha512-ULpB87ImNAiX36OMtyDeRceWB7N/mVlh6gGLqp/lx8UMKZlLIQH/UAFND86hYXHwNpXeNKcWfMGreb0Oc0hcZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primevue/core": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.3.6.tgz",
|
||||
"integrity": "sha512-ZuP0gqpEbIkpz9Em/O4Du+fRj0qyOl2YYuxhlELAtRg8+YkMsSJDd2ai2GM623sYRWOIwMr5rWevZGB1WqukzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/styled": "^0.7.0",
|
||||
"@primeuix/utils": "^0.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primevue/icons": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@primevue/icons/-/icons-4.3.6.tgz",
|
||||
"integrity": "sha512-QVFmfikMpo4/DObMSbB5kS8MH1OoQrx8N9prEZaMvfFzD3hixxK24l2VrcS5x5/0NnP3szwZwTCmEAutCygX6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/utils": "^0.6.0",
|
||||
"@primevue/core": "4.3.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.19",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz",
|
||||
@@ -2591,6 +2663,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@wdns/vue-code-block": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@wdns/vue-code-block/-/vue-code-block-2.3.5.tgz",
|
||||
"integrity": "sha512-09TguXcFZEA/9Fyx4c/3Jp4XUK82OKiT1TwH8cBeGg6Nj1E1R16nuABq1yJSvq/GZCxXtOJWw9i3TdxV4MctOw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/webdevnerdstuff"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/WebDevNerdStuff"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"highlight.js": "^11.8.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"ua-parser-js": "^1.0.38",
|
||||
"vue": "^3.4.31"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
@@ -3681,6 +3775,15 @@
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hookable": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||
@@ -4838,6 +4941,31 @@
|
||||
"integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/primevue": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/primevue/-/primevue-4.3.6.tgz",
|
||||
"integrity": "sha512-Wwg2dH6pBmOdkj9L/OnrCQf9AKPHfY5CcfnDyWeh0tNlR+XjYKGl8qvMdJOvGO9jjg6UdsX5MSaU8vDDsSG+sg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/styled": "^0.7.0",
|
||||
"@primeuix/styles": "^1.2.0",
|
||||
"@primeuix/utils": "^0.6.0",
|
||||
"@primevue/core": "4.3.6",
|
||||
"@primevue/icons": "4.3.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
||||
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@@ -5309,6 +5437,32 @@
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "1.0.40",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz",
|
||||
"integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ua-parser-js"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/faisalman"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/faisalman"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"ua-parser-js": "script/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
@@ -5671,6 +5825,12 @@
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-sonner": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-sonner/-/vue-sonner-2.0.1.tgz",
|
||||
"integrity": "sha512-sn4vjCRzRcnMaxaLa9aNSyZQi6S+gshiea5Lc3eqpkj0ES9LH8ljg+WJCkxefr28V4PZ9xkUXBIWpxGfQxstIg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue-tsc": {
|
||||
"version": "2.2.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz",
|
||||
|
||||
@@ -12,11 +12,15 @@
|
||||
"lint": "eslint . --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@primeuix/themes": "^1.2.1",
|
||||
"@wdns/vue-code-block": "^2.3.5",
|
||||
"gsap": "^3.13.0",
|
||||
"ogl": "^1.0.11",
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "^4.3.6",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-sonner": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg width="67" height="58" viewBox="0 0 67 58" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.948242 0.144043H22.837C22.837 0.144043 32.0299 17.1763 38.658 27.6444C38.9372 28.0853 39.0747 28.3453 39.3749 28.7722C39.6552 29.1709 39.813 29.3963 40.1365 29.7609C40.4407 30.1038 40.3868 30.0701 40.7353 30.4551C40.9256 30.6654 41.0901 30.8243 41.4999 31.0893C41.8709 31.3291 42.5065 31.4903 42.9886 31.3276C43.3618 31.2017 43.4628 31.1547 43.7801 30.9768C44.8913 30.354 46.0324 28.0858 46.0324 28.0858L58.7905 4.81785H47.6028V0.144994L66.7607 0.144043L51.406 26.7942C51.406 26.7942 49.8437 29.2994 48.7246 31.2443C48.5422 31.5613 47.7633 32.7078 47.46 33.0943C46.8867 33.825 46.5664 34.2616 46.0324 34.7887C45.5209 35.2936 45.0161 35.5409 44.6578 35.6848C44.1037 35.9073 43.511 36.0194 42.9158 36.068C42.4088 36.1093 42.0672 36.1359 41.563 36.068C40.9342 35.9833 40.8891 35.9616 40.1365 35.6848C39.82 35.5684 39.2914 35.2543 38.7932 34.8951C38.2398 34.496 37.8624 34.1401 37.2402 33.482C36.6925 32.9026 36.4509 32.5591 36.008 31.8962C35.543 31.2003 35.1813 30.8154 34.7421 30.103C28.7742 20.4232 20.2999 4.81785 20.2999 4.81785H9.0302L33.8982 48.0406L38.658 39.8352C38.658 39.8352 39.3105 40.7251 41.6 40.6509C43.7041 40.5826 44.2102 39.8352 44.2102 39.8352L33.8982 57.384L5.50866 8.16941L3.60187 4.81785L0.948242 0.144043Z" fill="#0e0e0e"/>
|
||||
<circle cx="42.4365" cy="14.686" r="4.78514" fill="#0e0e0e"/>
|
||||
<path d="M0.948242 0.144043H22.837C22.837 0.144043 32.0299 17.1763 38.658 27.6444C38.9372 28.0853 39.0747 28.3453 39.3749 28.7722C39.6552 29.1709 39.813 29.3963 40.1365 29.7609C40.4407 30.1038 40.3868 30.0701 40.7353 30.4551C40.9256 30.6654 41.0901 30.8243 41.4999 31.0893C41.8709 31.3291 42.5065 31.4903 42.9886 31.3276C43.3618 31.2017 43.4628 31.1547 43.7801 30.9768C44.8913 30.354 46.0324 28.0858 46.0324 28.0858L58.7905 4.81785H47.6028V0.144994L66.7607 0.144043L51.406 26.7942C51.406 26.7942 49.8437 29.2994 48.7246 31.2443C48.5422 31.5613 47.7633 32.7078 47.46 33.0943C46.8867 33.825 46.5664 34.2616 46.0324 34.7887C45.5209 35.2936 45.0161 35.5409 44.6578 35.6848C44.1037 35.9073 43.511 36.0194 42.9158 36.068C42.4088 36.1093 42.0672 36.1359 41.563 36.068C40.9342 35.9833 40.8891 35.9616 40.1365 35.6848C39.82 35.5684 39.2914 35.2543 38.7932 34.8951C38.2398 34.496 37.8624 34.1401 37.2402 33.482C36.6925 32.9026 36.4509 32.5591 36.008 31.8962C35.543 31.2003 35.1813 30.8154 34.7421 30.103C28.7742 20.4232 20.2999 4.81785 20.2999 4.81785H9.0302L33.8982 48.0406L38.658 39.8352C38.658 39.8352 39.3105 40.7251 41.6 40.6509C43.7041 40.5826 44.2102 39.8352 44.2102 39.8352L33.8982 57.384L5.50866 8.16941L3.60187 4.81785L0.948242 0.144043Z" fill="#0b0b0b"/>
|
||||
<circle cx="42.4365" cy="14.686" r="4.78514" fill="#0b0b0b"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,11 +1,11 @@
|
||||
<svg width="849" height="178" viewBox="0 0 849 178" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M292.054 150.096L247.575 30.3442H266.051L302.318 132.304L338.928 30.3442H357.062L312.583 150.096H292.054Z" fill="#0e0e0e"/>
|
||||
<path d="M388.782 152.149C382.053 152.149 376.179 150.78 371.161 148.043C366.257 145.306 362.436 141.2 359.699 135.726C357.076 130.251 355.765 123.351 355.765 115.026V63.8746H372.872V113.144C372.872 121.241 374.64 127.343 378.175 131.449C381.711 135.554 386.786 137.607 393.401 137.607C397.849 137.607 401.84 136.524 405.376 134.357C409.025 132.19 411.877 129.054 413.93 124.948C415.982 120.842 417.009 115.824 417.009 109.893V63.8746H434.116V150.096H418.891L417.693 135.383C415.07 140.63 411.249 144.735 406.231 147.701C401.213 150.666 395.397 152.149 388.782 152.149Z" fill="#0e0e0e"/>
|
||||
<path d="M486.973 152.149C478.762 152.149 471.463 150.267 465.076 146.503C458.689 142.74 453.671 137.493 450.022 130.764C446.486 124.035 444.718 116.223 444.718 107.327C444.718 98.2034 446.486 90.277 450.022 83.5481C453.671 76.7051 458.689 71.4019 465.076 67.6383C471.463 63.7606 478.876 61.8218 487.316 61.8218C495.755 61.8218 502.997 63.7036 509.042 67.4672C515.087 71.2308 519.763 76.249 523.07 82.5217C526.377 88.6803 528.031 95.5232 528.031 103.05C528.031 104.191 527.974 105.445 527.86 106.814C527.86 108.069 527.803 109.494 527.689 111.091H457.207V98.9447H510.924C510.582 91.7596 508.187 86.1712 503.739 82.1795C499.291 78.0737 493.759 76.0209 487.145 76.0209C482.468 76.0209 478.192 77.1043 474.314 79.2712C470.436 81.3241 467.3 84.4035 464.905 88.5092C462.624 92.5009 461.483 97.5761 461.483 103.735V108.525C461.483 114.912 462.624 120.329 464.905 124.777C467.3 129.111 470.436 132.418 474.314 134.699C478.192 136.866 482.411 137.949 486.973 137.949C492.448 137.949 496.953 136.752 500.488 134.357C504.024 131.962 506.647 128.712 508.358 124.606H525.465C523.982 129.852 521.473 134.585 517.938 138.805C514.402 142.911 510.011 146.161 504.765 148.556C499.633 150.951 493.702 152.149 486.973 152.149Z" fill="#0e0e0e"/>
|
||||
<path d="M575.061 150.096V30.3442H619.369C627.694 30.3442 634.651 31.7128 640.24 34.45C645.828 37.0731 649.991 40.7227 652.728 45.3987C655.579 49.9607 657.005 55.1499 657.005 60.9664C657.005 67.011 655.693 72.0862 653.07 76.1919C650.447 80.2977 646.968 83.4911 642.635 85.772C638.415 87.939 633.853 89.1935 628.949 89.5357L631.344 87.8249C636.59 87.939 641.38 89.3646 645.714 92.1018C650.048 94.7249 653.469 98.2604 655.978 102.708C658.487 107.156 659.742 112.06 659.742 117.421C659.742 123.579 658.259 129.168 655.294 134.186C652.329 139.09 647.938 142.968 642.121 145.819C636.305 148.67 629.177 150.096 620.737 150.096H575.061ZM592.168 135.897H618.513C626.04 135.897 631.857 134.186 635.963 130.764C640.183 127.229 642.292 122.268 642.292 115.881C642.292 109.608 640.125 104.59 635.792 100.827C631.572 97.0629 625.698 95.1811 618.171 95.1811H592.168V135.897ZM592.168 82.0084H617.487C624.672 82.0084 630.146 80.3547 633.91 77.0473C637.673 73.6258 639.555 68.9498 639.555 63.0193C639.555 57.3168 637.673 52.8119 633.91 49.5045C630.146 46.083 624.501 44.3722 616.974 44.3722H592.168V82.0084Z" fill="#0e0e0e"/>
|
||||
<path d="M677.064 150.096V63.8746H694.172V150.096H677.064ZM685.789 47.6226C682.482 47.6226 679.745 46.5962 677.578 44.5433C675.525 42.4904 674.498 39.8673 674.498 36.6739C674.498 33.5946 675.525 31.0855 677.578 29.1467C679.745 27.0938 682.482 26.0674 685.789 26.0674C688.983 26.0674 691.663 27.0938 693.83 29.1467C695.997 31.0855 697.08 33.5946 697.08 36.6739C697.08 39.8673 695.997 42.4904 693.83 44.5433C691.663 46.5962 688.983 47.6226 685.789 47.6226Z" fill="#0e0e0e"/>
|
||||
<path d="M750.797 150.096C745.323 150.096 740.59 149.24 736.598 147.53C732.606 145.819 729.527 142.968 727.36 138.976C725.193 134.984 724.11 129.567 724.11 122.724V78.4159H709.226V63.8746H724.11L726.163 42.3194H741.217V63.8746H765.681V78.4159H741.217V122.895C741.217 127.799 742.243 131.164 744.296 132.988C746.349 134.699 749.885 135.554 754.903 135.554H764.825V150.096H750.797Z" fill="#0e0e0e"/>
|
||||
<path d="M814.483 152.149C807.184 152.149 800.797 150.951 795.323 148.556C789.849 146.161 785.515 142.797 782.321 138.463C779.128 134.129 777.189 129.054 776.505 123.237H793.954C794.525 125.974 795.608 128.483 797.205 130.764C798.916 133.045 801.197 134.87 804.048 136.239C807.013 137.607 810.492 138.292 814.483 138.292C818.247 138.292 821.326 137.778 823.721 136.752C826.23 135.611 828.055 134.129 829.196 132.304C830.336 130.365 830.906 128.312 830.906 126.145C830.906 122.952 830.108 120.557 828.511 118.96C827.029 117.25 824.748 115.938 821.668 115.026C818.703 113.999 815.111 113.087 810.891 112.288C806.899 111.604 803.021 110.692 799.258 109.551C795.608 108.297 792.301 106.757 789.335 104.932C786.484 103.108 784.203 100.827 782.493 98.0893C780.782 95.2381 779.926 91.7596 779.926 87.6539C779.926 82.7497 781.238 78.3589 783.861 74.4812C786.484 70.4895 790.191 67.4102 794.981 65.2432C799.885 62.9622 805.644 61.8218 812.259 61.8218C821.839 61.8218 829.538 64.1027 835.354 68.6647C841.171 73.2267 844.592 79.6704 845.619 87.996H829.025C828.568 84.1183 826.858 81.1531 823.892 79.1002C820.927 76.9332 816.992 75.8498 812.088 75.8498C807.184 75.8498 803.421 76.8192 800.797 78.758C798.174 80.6969 796.863 83.263 796.863 86.4563C796.863 88.5092 797.604 90.334 799.087 91.9307C800.569 93.5274 802.736 94.896 805.587 96.0365C808.553 97.0629 812.145 98.0323 816.365 98.9447C822.41 100.085 827.827 101.511 832.617 103.222C837.407 104.932 841.228 107.441 844.079 110.749C846.93 114.056 848.356 118.789 848.356 124.948C848.47 130.308 847.101 135.041 844.25 139.147C841.513 143.253 837.578 146.446 832.446 148.727C827.428 151.008 821.44 152.149 814.483 152.149Z" fill="#0e0e0e"/>
|
||||
<path d="M0.345215 0.90332H67.8054C67.8054 0.90332 96.1375 53.396 116.565 85.658C117.425 87.017 117.849 87.8181 118.774 89.1338C119.638 90.3625 120.125 91.0574 121.121 92.1809C122.059 93.2378 121.893 93.1339 122.967 94.3206C123.553 94.9687 124.06 95.4584 125.323 96.275C126.467 97.0143 128.426 97.5109 129.911 97.0097C131.062 96.6216 131.373 96.4766 132.351 95.9285C135.776 94.0089 139.292 87.0185 139.292 87.0185L178.612 15.3078H144.132V0.90625L203.176 0.90332L155.854 83.038C155.854 83.038 151.039 90.7587 147.59 96.7529C147.028 97.7296 144.627 101.263 143.692 102.454C141.925 104.706 140.938 106.052 139.292 107.677C137.716 109.233 136.16 109.995 135.056 110.438C133.348 111.124 131.522 111.47 129.687 111.619C128.124 111.747 127.072 111.828 125.518 111.619C123.58 111.358 123.441 111.291 121.121 110.438C120.146 110.079 118.517 109.111 116.982 108.004C115.276 106.774 114.113 105.678 112.195 103.649C110.507 101.864 109.763 100.805 108.398 98.7619C106.965 96.6173 105.85 95.4309 104.496 93.2355C86.1035 63.4026 59.9861 15.3078 59.9861 15.3078H25.2534L101.895 148.518L116.565 123.23C116.565 123.23 118.576 125.972 125.632 125.743C132.117 125.533 133.676 123.23 133.676 123.23L101.895 177.314L14.4002 25.6371L8.52356 15.3078L0.345215 0.90332Z" fill="#0e0e0e"/>
|
||||
<circle cx="128.21" cy="45.7212" r="14.7476" fill="#0e0e0e"/>
|
||||
<path d="M292.054 150.096L247.575 30.3442H266.051L302.318 132.304L338.928 30.3442H357.062L312.583 150.096H292.054Z" fill="#0b0b0b"/>
|
||||
<path d="M388.782 152.149C382.053 152.149 376.179 150.78 371.161 148.043C366.257 145.306 362.436 141.2 359.699 135.726C357.076 130.251 355.765 123.351 355.765 115.026V63.8746H372.872V113.144C372.872 121.241 374.64 127.343 378.175 131.449C381.711 135.554 386.786 137.607 393.401 137.607C397.849 137.607 401.84 136.524 405.376 134.357C409.025 132.19 411.877 129.054 413.93 124.948C415.982 120.842 417.009 115.824 417.009 109.893V63.8746H434.116V150.096H418.891L417.693 135.383C415.07 140.63 411.249 144.735 406.231 147.701C401.213 150.666 395.397 152.149 388.782 152.149Z" fill="#0b0b0b"/>
|
||||
<path d="M486.973 152.149C478.762 152.149 471.463 150.267 465.076 146.503C458.689 142.74 453.671 137.493 450.022 130.764C446.486 124.035 444.718 116.223 444.718 107.327C444.718 98.2034 446.486 90.277 450.022 83.5481C453.671 76.7051 458.689 71.4019 465.076 67.6383C471.463 63.7606 478.876 61.8218 487.316 61.8218C495.755 61.8218 502.997 63.7036 509.042 67.4672C515.087 71.2308 519.763 76.249 523.07 82.5217C526.377 88.6803 528.031 95.5232 528.031 103.05C528.031 104.191 527.974 105.445 527.86 106.814C527.86 108.069 527.803 109.494 527.689 111.091H457.207V98.9447H510.924C510.582 91.7596 508.187 86.1712 503.739 82.1795C499.291 78.0737 493.759 76.0209 487.145 76.0209C482.468 76.0209 478.192 77.1043 474.314 79.2712C470.436 81.3241 467.3 84.4035 464.905 88.5092C462.624 92.5009 461.483 97.5761 461.483 103.735V108.525C461.483 114.912 462.624 120.329 464.905 124.777C467.3 129.111 470.436 132.418 474.314 134.699C478.192 136.866 482.411 137.949 486.973 137.949C492.448 137.949 496.953 136.752 500.488 134.357C504.024 131.962 506.647 128.712 508.358 124.606H525.465C523.982 129.852 521.473 134.585 517.938 138.805C514.402 142.911 510.011 146.161 504.765 148.556C499.633 150.951 493.702 152.149 486.973 152.149Z" fill="#0b0b0b"/>
|
||||
<path d="M575.061 150.096V30.3442H619.369C627.694 30.3442 634.651 31.7128 640.24 34.45C645.828 37.0731 649.991 40.7227 652.728 45.3987C655.579 49.9607 657.005 55.1499 657.005 60.9664C657.005 67.011 655.693 72.0862 653.07 76.1919C650.447 80.2977 646.968 83.4911 642.635 85.772C638.415 87.939 633.853 89.1935 628.949 89.5357L631.344 87.8249C636.59 87.939 641.38 89.3646 645.714 92.1018C650.048 94.7249 653.469 98.2604 655.978 102.708C658.487 107.156 659.742 112.06 659.742 117.421C659.742 123.579 658.259 129.168 655.294 134.186C652.329 139.09 647.938 142.968 642.121 145.819C636.305 148.67 629.177 150.096 620.737 150.096H575.061ZM592.168 135.897H618.513C626.04 135.897 631.857 134.186 635.963 130.764C640.183 127.229 642.292 122.268 642.292 115.881C642.292 109.608 640.125 104.59 635.792 100.827C631.572 97.0629 625.698 95.1811 618.171 95.1811H592.168V135.897ZM592.168 82.0084H617.487C624.672 82.0084 630.146 80.3547 633.91 77.0473C637.673 73.6258 639.555 68.9498 639.555 63.0193C639.555 57.3168 637.673 52.8119 633.91 49.5045C630.146 46.083 624.501 44.3722 616.974 44.3722H592.168V82.0084Z" fill="#0b0b0b"/>
|
||||
<path d="M677.064 150.096V63.8746H694.172V150.096H677.064ZM685.789 47.6226C682.482 47.6226 679.745 46.5962 677.578 44.5433C675.525 42.4904 674.498 39.8673 674.498 36.6739C674.498 33.5946 675.525 31.0855 677.578 29.1467C679.745 27.0938 682.482 26.0674 685.789 26.0674C688.983 26.0674 691.663 27.0938 693.83 29.1467C695.997 31.0855 697.08 33.5946 697.08 36.6739C697.08 39.8673 695.997 42.4904 693.83 44.5433C691.663 46.5962 688.983 47.6226 685.789 47.6226Z" fill="#0b0b0b"/>
|
||||
<path d="M750.797 150.096C745.323 150.096 740.59 149.24 736.598 147.53C732.606 145.819 729.527 142.968 727.36 138.976C725.193 134.984 724.11 129.567 724.11 122.724V78.4159H709.226V63.8746H724.11L726.163 42.3194H741.217V63.8746H765.681V78.4159H741.217V122.895C741.217 127.799 742.243 131.164 744.296 132.988C746.349 134.699 749.885 135.554 754.903 135.554H764.825V150.096H750.797Z" fill="#0b0b0b"/>
|
||||
<path d="M814.483 152.149C807.184 152.149 800.797 150.951 795.323 148.556C789.849 146.161 785.515 142.797 782.321 138.463C779.128 134.129 777.189 129.054 776.505 123.237H793.954C794.525 125.974 795.608 128.483 797.205 130.764C798.916 133.045 801.197 134.87 804.048 136.239C807.013 137.607 810.492 138.292 814.483 138.292C818.247 138.292 821.326 137.778 823.721 136.752C826.23 135.611 828.055 134.129 829.196 132.304C830.336 130.365 830.906 128.312 830.906 126.145C830.906 122.952 830.108 120.557 828.511 118.96C827.029 117.25 824.748 115.938 821.668 115.026C818.703 113.999 815.111 113.087 810.891 112.288C806.899 111.604 803.021 110.692 799.258 109.551C795.608 108.297 792.301 106.757 789.335 104.932C786.484 103.108 784.203 100.827 782.493 98.0893C780.782 95.2381 779.926 91.7596 779.926 87.6539C779.926 82.7497 781.238 78.3589 783.861 74.4812C786.484 70.4895 790.191 67.4102 794.981 65.2432C799.885 62.9622 805.644 61.8218 812.259 61.8218C821.839 61.8218 829.538 64.1027 835.354 68.6647C841.171 73.2267 844.592 79.6704 845.619 87.996H829.025C828.568 84.1183 826.858 81.1531 823.892 79.1002C820.927 76.9332 816.992 75.8498 812.088 75.8498C807.184 75.8498 803.421 76.8192 800.797 78.758C798.174 80.6969 796.863 83.263 796.863 86.4563C796.863 88.5092 797.604 90.334 799.087 91.9307C800.569 93.5274 802.736 94.896 805.587 96.0365C808.553 97.0629 812.145 98.0323 816.365 98.9447C822.41 100.085 827.827 101.511 832.617 103.222C837.407 104.932 841.228 107.441 844.079 110.749C846.93 114.056 848.356 118.789 848.356 124.948C848.47 130.308 847.101 135.041 844.25 139.147C841.513 143.253 837.578 146.446 832.446 148.727C827.428 151.008 821.44 152.149 814.483 152.149Z" fill="#0b0b0b"/>
|
||||
<path d="M0.345215 0.90332H67.8054C67.8054 0.90332 96.1375 53.396 116.565 85.658C117.425 87.017 117.849 87.8181 118.774 89.1338C119.638 90.3625 120.125 91.0574 121.121 92.1809C122.059 93.2378 121.893 93.1339 122.967 94.3206C123.553 94.9687 124.06 95.4584 125.323 96.275C126.467 97.0143 128.426 97.5109 129.911 97.0097C131.062 96.6216 131.373 96.4766 132.351 95.9285C135.776 94.0089 139.292 87.0185 139.292 87.0185L178.612 15.3078H144.132V0.90625L203.176 0.90332L155.854 83.038C155.854 83.038 151.039 90.7587 147.59 96.7529C147.028 97.7296 144.627 101.263 143.692 102.454C141.925 104.706 140.938 106.052 139.292 107.677C137.716 109.233 136.16 109.995 135.056 110.438C133.348 111.124 131.522 111.47 129.687 111.619C128.124 111.747 127.072 111.828 125.518 111.619C123.58 111.358 123.441 111.291 121.121 110.438C120.146 110.079 118.517 109.111 116.982 108.004C115.276 106.774 114.113 105.678 112.195 103.649C110.507 101.864 109.763 100.805 108.398 98.7619C106.965 96.6173 105.85 95.4309 104.496 93.2355C86.1035 63.4026 59.9861 15.3078 59.9861 15.3078H25.2534L101.895 148.518L116.565 123.23C116.565 123.23 118.576 125.972 125.632 125.743C132.117 125.533 133.676 123.23 133.676 123.23L101.895 177.314L14.4002 25.6371L8.52356 15.3078L0.345215 0.90332Z" fill="#0b0b0b"/>
|
||||
<circle cx="128.21" cy="45.7212" r="14.7476" fill="#0b0b0b"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
144
src/components/code/CliInstallation.vue
Normal file
144
src/components/code/CliInstallation.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div class="cli-installation">
|
||||
<h2 class="demo-title">One-Time Installation</h2>
|
||||
|
||||
<VCodeBlock v-if="command" :code="command" :persistent-copy-button="true" highlightjs lang="bash" theme="nord"
|
||||
:copy-button="true" class="code-block" />
|
||||
|
||||
<div class="cli-divider"></div>
|
||||
|
||||
<h2 class="demo-title">Full CLI Setup</h2>
|
||||
<p class="jsrepo-info">
|
||||
Vue Bits uses
|
||||
<a href="https://jsrepo.dev/" target="_blank" rel="noreferrer">jsrepo</a>
|
||||
to help you install components via CLI— you can set it up project-wide following the steps below.
|
||||
</p>
|
||||
|
||||
<Accordion expandIcon="pi pi-chevron-right" collapseIcon="pi pi-chevron-down">
|
||||
<AccordionPanel value="setup">
|
||||
<AccordionHeader>Setup Steps</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div class="setup-content">
|
||||
<p class="demo-extra-info">1. Initialize a config file for your project</p>
|
||||
|
||||
<div class="setup-option">
|
||||
<VCodeBlock :persistent-copy-button="true" code="npx jsrepo init https://vuebits.dev" highlightjs
|
||||
lang="bash" theme="nord" :copy-button="true" class="code-block" />
|
||||
</div>
|
||||
|
||||
<p class="demo-extra-info">2. Browse & add components from the list</p>
|
||||
<VCodeBlock :persistent-copy-button="true" code="npx jsrepo add" highlightjs lang="bash" theme="nord"
|
||||
:copy-button="true" class="code-block" />
|
||||
|
||||
<p class="demo-extra-info">3. Or just add a specific component</p>
|
||||
<VCodeBlock :persistent-copy-button="true" code="npx jsrepo add Animations/AnimatedContainer" highlightjs
|
||||
lang="bash" theme="nord" :copy-button="true" class="code-block" />
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VCodeBlock } from '@wdns/vue-code-block'
|
||||
import Accordion from 'primevue/accordion'
|
||||
import AccordionPanel from 'primevue/accordionpanel'
|
||||
import AccordionHeader from 'primevue/accordionheader'
|
||||
import AccordionContent from 'primevue/accordioncontent'
|
||||
|
||||
const { command } = defineProps<{
|
||||
command?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cli-installation {
|
||||
padding-bottom: 1.2rem;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.setup-content {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.setup-option {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.jsrepo-info {
|
||||
color: #a1a1aa;
|
||||
font-size: 14px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.jsrepo-info a {
|
||||
color: #27FF64;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #142216;
|
||||
}
|
||||
|
||||
:deep(.p-accordion-header) {
|
||||
background: #0b0b0b;
|
||||
border: 1px solid #142216;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
:deep(.p-accordionpanel) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.p-accordionheader-toggle-icon) {
|
||||
color: #fff !important;
|
||||
fill: #fff !important;
|
||||
}
|
||||
|
||||
:deep(.p-accordionheader-toggle-icon svg) {
|
||||
color: #fff !important;
|
||||
fill: #fff !important;
|
||||
}
|
||||
|
||||
:deep(.p-accordionheader-toggle-icon path) {
|
||||
fill: #fff !important;
|
||||
stroke: #fff !important;
|
||||
}
|
||||
|
||||
:deep(.p-accordion-content) {
|
||||
background: #0b0b0b;
|
||||
border: 1px solid #142216;
|
||||
border-top: none;
|
||||
border-radius: 0 0 20px 20px;
|
||||
}
|
||||
|
||||
:deep(.v-code-block) {
|
||||
background: #0b0b0b;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.v-code-block--tab-highlightjs-github-dark-icon) {
|
||||
color: #999 !important;
|
||||
fill: #999 !important;
|
||||
}
|
||||
|
||||
:deep(.v-code-block--me-1) {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.v-code-block pre) {
|
||||
background: #0b0b0b;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.v-code-block .hljs) {
|
||||
background: #0b0b0b;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
110
src/components/code/CodeExample.vue
Normal file
110
src/components/code/CodeExample.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="code-example">
|
||||
<div v-for="[name, snippet] in codeEntries" :key="name" class="code-section">
|
||||
<h2 v-if="shouldShowTitle()" class="demo-title">{{ getDisplayName(name) }}</h2>
|
||||
|
||||
<VCodeBlock v-if="snippet" :code="snippet" highlightjs :lang="getLanguage(name)" theme="nord"
|
||||
:copy-button="true" :persistent-copy-button="true" class="code-block" />
|
||||
|
||||
<div v-if="!snippet" class="no-code">
|
||||
<span>Nothing here yet!</span>
|
||||
<i class="pi pi-face-sad"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { VCodeBlock } from '@wdns/vue-code-block'
|
||||
import type { CodeObject } from '../../types/code'
|
||||
|
||||
const props = defineProps<{
|
||||
codeObject: CodeObject
|
||||
}>()
|
||||
|
||||
const skipKeys = [
|
||||
'cli'
|
||||
]
|
||||
|
||||
const codeEntries = computed(() => {
|
||||
return Object.entries(props.codeObject).filter(([name]) => !skipKeys.includes(name))
|
||||
})
|
||||
|
||||
const shouldShowTitle = () => {
|
||||
return true // Show titles for all sections
|
||||
}
|
||||
|
||||
const getDisplayName = (name: string) => {
|
||||
if (name === 'code') return 'Code'
|
||||
if (name === 'cli') return 'CLI Command'
|
||||
if (name === 'utility') return 'Utility'
|
||||
if (name === 'usage') return 'Usage'
|
||||
if (name === 'installation') return 'Installation'
|
||||
return name.charAt(0).toUpperCase() + name.slice(1)
|
||||
}
|
||||
|
||||
const getLanguage = (name: string) => {
|
||||
if (name === 'cli') return 'bash'
|
||||
if (name === 'code') return 'html'
|
||||
if (name === 'usage') return 'html'
|
||||
if (name === 'installation') return 'bash'
|
||||
return 'javascript'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.code-example {
|
||||
margin: 1.2rem 0;
|
||||
}
|
||||
|
||||
.code-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
overflow: hidden;
|
||||
border: 1px solid #142216;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.no-code {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: #a1a1aa;
|
||||
font-style: italic;
|
||||
padding: 2rem;
|
||||
background: #0b0b0b;
|
||||
border: 1px solid #142216;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
:deep(.v-code-block) {
|
||||
background: #0b0b0b;
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.v-code-block pre) {
|
||||
background: #0b0b0b;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
:deep(.v-code-block--tab-highlightjs-github-dark-icon) {
|
||||
color: #999 !important;
|
||||
fill: #999 !important;
|
||||
}
|
||||
|
||||
:deep(.v-code-block--me-1) {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.v-code-block .hljs) {
|
||||
background: #0b0b0b;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
18
src/components/code/Dependencies.vue
Normal file
18
src/components/code/Dependencies.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="dependencies-container">
|
||||
<h2 class="demo-title">Dependencies</h2>
|
||||
<div class="demo-details">
|
||||
<span v-for="dep in dependencyList" :key="dep" class="dependency-tag">
|
||||
{{ dep }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
dependencyList: string[]
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
47
src/components/common/ContributionSection.vue
Normal file
47
src/components/common/ContributionSection.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="contribute-container">
|
||||
<h2 class="demo-title-contribute">Help improve this component!</h2>
|
||||
<div class="contribute-buttons">
|
||||
<a
|
||||
:href="bugReportUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
class="contribute-button"
|
||||
>
|
||||
<i class="pi pi-exclamation-triangle"></i>
|
||||
<span>Report an issue</span>
|
||||
</a>
|
||||
<span class="contribute-separator">or</span>
|
||||
<a
|
||||
:href="featureRequestUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
class="contribute-button"
|
||||
>
|
||||
<i class="pi pi-lightbulb"></i>
|
||||
<span>Request a feature</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const bugReportUrl = computed(() => {
|
||||
const category = route.params.category
|
||||
const subcategory = route.params.subcategory
|
||||
const title = encodeURIComponent(`[BUG]: ${category}/${subcategory}`)
|
||||
return `https://github.com/DavidHDev/vue-bits/issues/new?template=1-bug-report.yml&title=${title}&labels=bug`
|
||||
})
|
||||
|
||||
const featureRequestUrl = computed(() => {
|
||||
const category = route.params.category
|
||||
const subcategory = route.params.subcategory
|
||||
const title = encodeURIComponent(`[FEAT]: ${category}/${subcategory}`)
|
||||
return `https://github.com/DavidHDev/vue-bits/issues/new?template=2-feature-request.yml&title=${title}&labels=enhancement`
|
||||
})
|
||||
</script>
|
||||
6
src/components/common/Customize.vue
Normal file
6
src/components/common/Customize.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="demo-title">Customize</h2>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
118
src/components/common/PreviewSlider.vue
Normal file
118
src/components/common/PreviewSlider.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="preview-slider">
|
||||
<span class="slider-label">{{ title }}</span>
|
||||
<Slider
|
||||
:model-value="modelValue"
|
||||
@update:model-value="handleSliderChange"
|
||||
:min="min"
|
||||
:max="max"
|
||||
:step="step"
|
||||
:disabled="disabled"
|
||||
class="custom-slider"
|
||||
/>
|
||||
<span class="slider-value">{{ modelValue }}{{ valueUnit }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Slider from 'primevue/slider'
|
||||
|
||||
defineProps<{
|
||||
title: string
|
||||
modelValue: number
|
||||
min?: number
|
||||
max?: number
|
||||
step?: number
|
||||
valueUnit?: string
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: number]
|
||||
}>()
|
||||
|
||||
const handleSliderChange = (value: number | number[]) => {
|
||||
const numValue = Array.isArray(value) ? value[0] : value
|
||||
emit('update:modelValue', numValue)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.preview-slider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.slider-label {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.custom-slider {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.slider-value {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
min-width: 3rem;
|
||||
}
|
||||
|
||||
:deep(.p-slider) {
|
||||
background: #222;
|
||||
border-radius: 10px;
|
||||
height: 8px !important;
|
||||
border: 1px solid #333 !important;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.p-slider-range) {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.p-slider-handle) {
|
||||
background: #0b0b0b !important;
|
||||
border: 2px solid #fff !important;
|
||||
box-shadow: none !important;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
border-radius: 50%;
|
||||
transition: all 0.1s ease;
|
||||
outline: none !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:deep(.p-slider-handle:hover) {
|
||||
transform: scale(1.1);
|
||||
border: 2px solid #fff !important;
|
||||
box-shadow: 0 0 8px rgba(82, 39, 255, 0.3) !important;
|
||||
}
|
||||
|
||||
:deep(.p-slider-handle:focus) {
|
||||
outline: none !important;
|
||||
border: 2px solid #fff !important;
|
||||
box-shadow: 0 0 8px rgba(82, 39, 255, 0.5) !important;
|
||||
}
|
||||
|
||||
:deep(.p-slider-handle:active) {
|
||||
transform: scale(1.05);
|
||||
transition: all 0.05s ease;
|
||||
border: 2px solid #fff !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-slider-handle::before),
|
||||
:deep(.p-slider-handle::after) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-slider-handle > *) {
|
||||
border: none !important;
|
||||
outline: none !important;
|
||||
}
|
||||
</style>
|
||||
115
src/components/common/PreviewSwitch.vue
Normal file
115
src/components/common/PreviewSwitch.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="preview-switch">
|
||||
<span class="switch-label">{{ title }}</span>
|
||||
<ToggleSwitch
|
||||
:model-value="modelValue"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ToggleSwitch from 'primevue/toggleswitch'
|
||||
|
||||
defineProps<{
|
||||
title: string
|
||||
modelValue: boolean
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.preview-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.switch-label {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch) {
|
||||
width: 2.5rem;
|
||||
height: 1.25rem;
|
||||
border: 1px solid #333;
|
||||
border-radius: 10px;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch-slider) {
|
||||
background: #222;
|
||||
border-radius: 10px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch.p-toggleswitch-checked .p-toggleswitch-slider) {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch-handle) {
|
||||
background: #0b0b0b;
|
||||
border: 2px solid #fff;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 50%;
|
||||
transition: all 0.1s ease;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch:hover .p-toggleswitch-slider) {
|
||||
background: #222 !important;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch.p-toggleswitch-checked:hover .p-toggleswitch-slider) {
|
||||
background: #fff !important;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch-handle:hover) {
|
||||
background: #0b0b0b !important;
|
||||
border: 2px solid #fff !important;
|
||||
box-shadow: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch-handle:focus) {
|
||||
outline: none !important;
|
||||
background: #0b0b0b !important;
|
||||
border: 2px solid #fff !important;
|
||||
box-shadow: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch-handle:active) {
|
||||
background: #0b0b0b !important;
|
||||
border: 2px solid #fff !important;
|
||||
box-shadow: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch:focus) {
|
||||
outline: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch-handle::before),
|
||||
:deep(.p-toggleswitch-handle::after) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.p-toggleswitch-handle > *) {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
131
src/components/common/PropTable.vue
Normal file
131
src/components/common/PropTable.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="prop-table-container">
|
||||
<h2 class="demo-title">Props</h2>
|
||||
<div class="table-wrapper">
|
||||
<DataTable :value="data" class="props-table">
|
||||
<Column field="name" header="Property">
|
||||
<template #body="{ data }">
|
||||
<div class="code-cell">{{ data.name }}</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="type" header="Type">
|
||||
<template #body="{ data }">
|
||||
<span class="type-text">{{ data.type }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="default" header="Default">
|
||||
<template #body="{ data }">
|
||||
<div class="code-cell">{{ data.default || '—' }}</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="description" header="Description">
|
||||
<template #body="{ data }">
|
||||
<div class="description-text">{{ data.description }}</div>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Column from 'primevue/column'
|
||||
|
||||
interface PropData {
|
||||
name: string
|
||||
type: string
|
||||
default: string
|
||||
description: string
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
data: PropData[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.prop-table-container {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.code-cell {
|
||||
font-family: monospace;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 5px;
|
||||
width: fit-content;
|
||||
font-weight: 500;
|
||||
color: #e9e9e9;
|
||||
background-color: #222;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.type-text {
|
||||
font-family: monospace;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.description-text {
|
||||
max-width: 300px;
|
||||
color: #fff;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.p-datatable) {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.p-datatable-header) {
|
||||
background: #111;
|
||||
border: 1px solid #142216;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
:deep(.p-datatable-thead > tr > th) {
|
||||
background: #111;
|
||||
border-right: 1px solid #142216;
|
||||
border-bottom: 1px solid #142216;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
text-transform: capitalize;
|
||||
letter-spacing: -0.5px;
|
||||
padding: 1rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.p-datatable-thead > tr > th:last-child) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
:deep(.p-datatable-tbody > tr > td) {
|
||||
background: #0b0b0b;
|
||||
border-right: 1px solid #142216;
|
||||
border-bottom: 1px solid #142216;
|
||||
color: #fff;
|
||||
padding: 1rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.p-datatable-tbody > tr > td:last-child) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
:deep(.p-datatable-tbody > tr:last-child > td) {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
:deep(.p-datatable-tbody > tr:hover) {
|
||||
background: #0b0b0b;
|
||||
}
|
||||
</style>
|
||||
39
src/components/common/RefreshButton.vue
Normal file
39
src/components/common/RefreshButton.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<Button class="refresh-button" @click="$emit('refresh')" outlined rounded size="small">
|
||||
<i class="pi pi-refresh"></i>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
|
||||
defineEmits<{
|
||||
refresh: []
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.refresh-button {
|
||||
position: absolute;
|
||||
top: 0.75rem;
|
||||
right: 0.75rem;
|
||||
z-index: 2;
|
||||
background-color: #111;
|
||||
border: 1px solid #142216;
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
padding: 0.5rem;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.refresh-button:hover {
|
||||
background-color: #222 !important;
|
||||
color: #fff !important;
|
||||
border: 1px solid #142216 !important;
|
||||
outline: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
.refresh-button:active {
|
||||
background-color: #142216;
|
||||
}
|
||||
</style>
|
||||
166
src/components/common/TabbedLayout.vue
Normal file
166
src/components/common/TabbedLayout.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="tabbed-layout">
|
||||
<Tabs value="0">
|
||||
<TabList>
|
||||
<Tab value="0">
|
||||
<div class="tab-header">
|
||||
<i class="pi pi-eye"></i>
|
||||
<span>Preview</span>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab value="1">
|
||||
<div class="tab-header">
|
||||
<i class="pi pi-code"></i>
|
||||
<span>Code</span>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab value="2">
|
||||
<div class="tab-header">
|
||||
<i class="pi pi-box"></i>
|
||||
<span>CLI</span>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab value="3">
|
||||
<div class="tab-header">
|
||||
<i class="pi pi-heart"></i>
|
||||
<span>Contribute</span>
|
||||
</div>
|
||||
</Tab>
|
||||
</TabList>
|
||||
|
||||
<TabPanels>
|
||||
<TabPanel value="0">
|
||||
<slot name="preview" />
|
||||
</TabPanel>
|
||||
<TabPanel value="1">
|
||||
<slot name="code" />
|
||||
</TabPanel>
|
||||
<TabPanel value="2">
|
||||
<slot name="cli" />
|
||||
</TabPanel>
|
||||
<TabPanel value="3">
|
||||
<ContributionSection />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Tabs from 'primevue/tabs'
|
||||
import TabList from 'primevue/tablist'
|
||||
import Tab from 'primevue/tab'
|
||||
import TabPanels from 'primevue/tabpanels'
|
||||
import TabPanel from 'primevue/tabpanel'
|
||||
import ContributionSection from './ContributionSection.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tabbed-layout {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid #142216;
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
height: 36px;
|
||||
color: #ffffff;
|
||||
background: transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tab-header:hover {
|
||||
background: #142216;
|
||||
}
|
||||
|
||||
:deep(.p-tablist),
|
||||
:deep(.p-tablist-tab-list) {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
:deep(.p-tablist) {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
:deep(.p-tablist .p-tab:nth-child(1)),
|
||||
:deep(.p-tablist .p-tab:nth-child(2)),
|
||||
:deep(.p-tablist .p-tab:nth-child(3)) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
:deep(.p-tablist .p-tab:nth-child(4)) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
:deep(.p-tab) {
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.p-tab:not(.p-disabled):focus) {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-tab:hover) {
|
||||
background: transparent !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-tablist-active-bar),
|
||||
:deep(.p-tab-indicator),
|
||||
:deep(.p-tab::before),
|
||||
:deep(.p-tab::after),
|
||||
:deep(.p-tab[aria-selected="true"]::before),
|
||||
:deep(.p-tab[aria-selected="true"]::after),
|
||||
:deep(.p-tablist::after),
|
||||
:deep(.p-tablist-tab-list::before),
|
||||
:deep(.p-tablist-tab-list::after),
|
||||
:deep(.p-tab .p-tab-header-action::before),
|
||||
:deep(.p-tab .p-tab-header-action::after) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-tab[aria-selected="true"]) {
|
||||
background: transparent !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
:deep(.p-tab[aria-selected="true"] .tab-header) {
|
||||
background: #333333;
|
||||
color: #A7EF9E;
|
||||
}
|
||||
|
||||
:deep(.p-tabpanels) {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.p-tabpanel) {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:deep(.p-tablist) {
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
:deep(.p-tablist .p-tab:nth-child(4)) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -11,7 +11,7 @@
|
||||
padding: 0 4em;
|
||||
height: 160px;
|
||||
margin: 0 auto;
|
||||
background: linear-gradient(to bottom, #0e0e0e, transparent);
|
||||
background: linear-gradient(to bottom, #0b0b0b, transparent);
|
||||
}
|
||||
|
||||
.header-container {
|
||||
@@ -125,7 +125,7 @@
|
||||
}
|
||||
|
||||
.cta-button span {
|
||||
background-color: #0e0e0e;
|
||||
background-color: #0b0b0b;
|
||||
margin-left: 1em;
|
||||
margin-right: calc(1em - 8px);
|
||||
padding-top: .1em;
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
|
||||
.feature-card {
|
||||
user-select: none;
|
||||
background: #0e0e0e;
|
||||
background: #0b0b0b;
|
||||
border: 1px solid rgba(148, 184, 154, 0.2);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
@@ -357,7 +357,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 20%;
|
||||
background: linear-gradient(to top, #0e0e0e 0%, transparent 100%);
|
||||
background: linear-gradient(to top, #0b0b0b 0%, transparent 100%);
|
||||
border-radius: 0 0 8px 8px;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
@@ -370,7 +370,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 20%;
|
||||
background: linear-gradient(to bottom, #0e0e0e 0%, transparent 100%);
|
||||
background: linear-gradient(to bottom, #0b0b0b 0%, transparent 100%);
|
||||
border-radius: 8px 8px 0 0;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
@@ -410,7 +410,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 20%;
|
||||
background: linear-gradient(to top, #0e0e0e 0%, transparent 100%);
|
||||
background: linear-gradient(to top, #0b0b0b 0%, transparent 100%);
|
||||
border-radius: 0 0 8px 8px;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
@@ -423,7 +423,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 20%;
|
||||
background: linear-gradient(to bottom, #0e0e0e 0%, transparent 100%);
|
||||
background: linear-gradient(to bottom, #0b0b0b 0%, transparent 100%);
|
||||
border-radius: 8px 8px 0 0;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
@@ -462,7 +462,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 20%;
|
||||
background: linear-gradient(to top, #0e0e0e 0%, transparent 100%);
|
||||
background: linear-gradient(to top, #0b0b0b 0%, transparent 100%);
|
||||
border-radius: 0 0 8px 8px;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
@@ -475,7 +475,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 20%;
|
||||
background: linear-gradient(to bottom, #0e0e0e 0%, transparent 100%);
|
||||
background: linear-gradient(to bottom, #0b0b0b 0%, transparent 100%);
|
||||
border-radius: 8px 8px 0 0;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
margin-top: 8rem;
|
||||
padding: 2.4rem;
|
||||
border-top: 1px solid rgba(149, 184, 148, 0.1);
|
||||
background: linear-gradient(to bottom, transparent, #0e0e0e);
|
||||
background: linear-gradient(to bottom, transparent, #0b0b0b);
|
||||
z-index: 220;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<span>Browse Components</span>
|
||||
<div class="button-arrow-circle">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="#ffffff" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 12L10 8L6 4" stroke="#4c1d95" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M6 12L10 8L6 4" stroke="#0b0b0b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
}
|
||||
|
||||
.start-building-title {
|
||||
color: #0e0e0e;
|
||||
color: #0b0b0b;
|
||||
font-size: 2.6rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
@@ -65,7 +65,7 @@
|
||||
}
|
||||
|
||||
.start-building-subtitle {
|
||||
color: #0e0e0e;
|
||||
color: #0b0b0b;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin: -1rem 0 0 0;
|
||||
@@ -76,8 +76,8 @@
|
||||
|
||||
.start-building-button {
|
||||
background: transparent;
|
||||
color: #0e0e0e;
|
||||
border: 2px solid #0e0e0e;
|
||||
color: #0b0b0b;
|
||||
border: 2px solid #0b0b0b;
|
||||
padding: .6rem 1.6rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
@@ -87,7 +87,7 @@
|
||||
}
|
||||
|
||||
.start-building-button:hover {
|
||||
background: #0e0e0e;
|
||||
background: #0b0b0b;
|
||||
color: #27FF64;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
<template>
|
||||
<main class="app-container">
|
||||
<header>
|
||||
<h2>Header Component</h2>
|
||||
</header>
|
||||
<Header />
|
||||
<section class="category-wrapper">
|
||||
<aside>
|
||||
<h3>Sidebar Component</h3>
|
||||
</aside>
|
||||
<Sidebar />
|
||||
<div class="category-page">
|
||||
<router-view />
|
||||
</div>
|
||||
</section>
|
||||
<Toast position="bottom-right"
|
||||
:closeButtonProps="{ style: { justifyContent: 'flex-end', right: '0', margin: '0', outline: 'none', border: 'none' } }"
|
||||
:pt="{
|
||||
message: {
|
||||
style: {
|
||||
borderRadius: '10px'
|
||||
}
|
||||
},
|
||||
messageContent: {
|
||||
style: {
|
||||
alignItems: 'center'
|
||||
}
|
||||
},
|
||||
messageIcon: {
|
||||
style: {
|
||||
display: 'none'
|
||||
}
|
||||
}
|
||||
}" />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// Layout component for category pages
|
||||
import Header from '../navs/Header.vue';
|
||||
import Sidebar from '../navs/Sidebar.vue';
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.category-wrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
aside {
|
||||
width: 250px;
|
||||
background-color: #f5f5f5;
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
231
src/components/navs/Header.vue
Normal file
231
src/components/navs/Header.vue
Normal file
@@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<div class="main-nav">
|
||||
<div class="nav-items">
|
||||
<router-link to="/" class="logo">
|
||||
<img :src="Logo" alt="Logo" />
|
||||
</router-link>
|
||||
|
||||
<button class="mobile-menu-button" aria-label="Open Menu" @click="toggleDrawer">
|
||||
<i class="pi pi-bars"></i>
|
||||
</button>
|
||||
|
||||
<div class="desktop-nav">
|
||||
<!-- TODO: Search Component -->
|
||||
|
||||
<FadeContent blur>
|
||||
<button class="cta-button-docs" @click="openGitHub">
|
||||
Star On GitHub
|
||||
<span class="star-count">
|
||||
<img :src="Star" alt="Star Icon" />
|
||||
{{ stars }}
|
||||
</span>
|
||||
</button>
|
||||
</FadeContent>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isDrawerOpen" class="drawer-overlay" @click="closeDrawer">
|
||||
<div class="drawer-content" @click.stop>
|
||||
<div class="drawer-header">
|
||||
<img :src="Logo" alt="Logo" class="drawer-logo" />
|
||||
<button class="close-button" aria-label="Close Menu" @click="closeDrawer">
|
||||
<i class="pi pi-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="drawer-body">
|
||||
<!-- Navigation Categories -->
|
||||
<div class="drawer-navigation">
|
||||
<div class="categories-container">
|
||||
<Category
|
||||
v-for="cat in CATEGORIES"
|
||||
:key="cat.name"
|
||||
:category="cat"
|
||||
:location="route"
|
||||
:handle-click="onNavClick"
|
||||
:handle-transition-navigation="handleMobileTransitionNavigation"
|
||||
:on-item-mouse-enter="() => {}"
|
||||
:on-item-mouse-leave="() => {}"
|
||||
:is-transitioning="isTransitioning"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-separator"></div>
|
||||
|
||||
<div class="drawer-section">
|
||||
<p class="section-title">Useful Links</p>
|
||||
<router-link to="/text-animations/split-text" @click="closeDrawer" class="drawer-link">
|
||||
Docs
|
||||
</router-link>
|
||||
<a href="https://github.com/DavidHDev/vue-bits" target="_blank" @click="closeDrawer" class="drawer-link">
|
||||
GitHub
|
||||
<i class="pi pi-arrow-up-right arrow-icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="drawer-separator"></div>
|
||||
|
||||
<div class="drawer-section">
|
||||
<p class="section-title">Other</p>
|
||||
<a href="https://davidhaz.com/" target="_blank" @click="closeDrawer" class="drawer-link">
|
||||
Who made this?
|
||||
<i class="pi pi-arrow-up-right arrow-icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed, defineComponent, h } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStars } from '../../composables/useStars'
|
||||
import { CATEGORIES, NEW, UPDATED } from '../../constants/Categories'
|
||||
import FadeContent from '../../content/Animations/FadeContent/FadeContent.vue'
|
||||
import Logo from '../../assets/logos/vue-bits-logo.svg'
|
||||
import Star from '../../assets/common/star.svg'
|
||||
|
||||
const isDrawerOpen = ref(false)
|
||||
const isTransitioning = ref(false)
|
||||
const stars = useStars()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// Helper function
|
||||
const slug = (str: string) => str.replace(/\s+/g, "-").toLowerCase()
|
||||
|
||||
const toggleDrawer = () => {
|
||||
isDrawerOpen.value = !isDrawerOpen.value
|
||||
}
|
||||
|
||||
const closeDrawer = () => {
|
||||
isDrawerOpen.value = false
|
||||
}
|
||||
|
||||
const openGitHub = () => {
|
||||
window.open('https://github.com/DavidHDev/vue-bits', '_blank')
|
||||
}
|
||||
|
||||
const onNavClick = () => {
|
||||
closeDrawer()
|
||||
window.scrollTo(0, 0)
|
||||
}
|
||||
|
||||
const handleMobileTransitionNavigation = async (path: string) => {
|
||||
if (isTransitioning.value || route.path === path) return
|
||||
|
||||
closeDrawer()
|
||||
isTransitioning.value = true
|
||||
|
||||
try {
|
||||
await router.push(path)
|
||||
window.scrollTo(0, 0)
|
||||
} finally {
|
||||
isTransitioning.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && isDrawerOpen.value) {
|
||||
closeDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
// Category Component
|
||||
const Category = defineComponent({
|
||||
name: 'Category',
|
||||
props: {
|
||||
category: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
location: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
handleClick: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
handleTransitionNavigation: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
onItemMouseEnter: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
onItemMouseLeave: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
isTransitioning: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
interface ItemType {
|
||||
sub: string
|
||||
path: string
|
||||
isActive: boolean
|
||||
isNew: boolean
|
||||
isUpdated: boolean
|
||||
}
|
||||
|
||||
const items = computed(() =>
|
||||
props.category.subcategories.map((sub: string): ItemType => {
|
||||
const path = `/${slug(props.category.name)}/${slug(sub)}`
|
||||
const activePath = props.location.path
|
||||
return {
|
||||
sub,
|
||||
path,
|
||||
isActive: activePath === path,
|
||||
isNew: (NEW as string[]).includes(sub),
|
||||
isUpdated: (UPDATED as string[]).includes(sub),
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return () => h('div', { class: 'category' }, [
|
||||
h('p', { class: 'category-name' }, props.category.name),
|
||||
h('div', { class: 'category-items' },
|
||||
items.value.map(({ sub, path, isActive, isNew, isUpdated }: ItemType) => {
|
||||
return h('router-link', {
|
||||
key: path,
|
||||
to: path,
|
||||
class: [
|
||||
'sidebar-item',
|
||||
{ 'active-sidebar-item': isActive },
|
||||
{ 'transitioning': props.isTransitioning }
|
||||
],
|
||||
onClick: (e: Event) => {
|
||||
e.preventDefault()
|
||||
props.handleTransitionNavigation(path)
|
||||
},
|
||||
onMouseenter: (e: Event) => props.onItemMouseEnter(path, e),
|
||||
onMouseleave: props.onItemMouseLeave
|
||||
}, {
|
||||
default: () => [
|
||||
sub,
|
||||
isNew ? h('span', { class: 'new-tag' }, 'New') : null,
|
||||
isUpdated ? h('span', { class: 'updated-tag' }, 'Updated') : null
|
||||
].filter(Boolean)
|
||||
})
|
||||
})
|
||||
)
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
</script>
|
||||
334
src/components/navs/Sidebar.vue
Normal file
334
src/components/navs/Sidebar.vue
Normal file
@@ -0,0 +1,334 @@
|
||||
<template>
|
||||
<!-- Mobile Drawer -->
|
||||
<div v-if="isDrawerOpen" class="drawer-overlay" @click="closeDrawer">
|
||||
<div class="drawer-content" :class="{ 'drawer-open': isDrawerOpen }" @click.stop>
|
||||
<div class="drawer-header sidebar-logo">
|
||||
<div class="drawer-header-content">
|
||||
<router-link to="/" @click="closeDrawer">
|
||||
<img :src="Logo" alt="Logo" class="drawer-logo" />
|
||||
</router-link>
|
||||
<button class="icon-button" aria-label="Close" @click="closeDrawer">
|
||||
<i class="pi pi-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-body">
|
||||
<div class="categories-container">
|
||||
<Category v-for="cat in CATEGORIES" :key="cat.name" :category="cat" :location="route"
|
||||
:pending-active-path="pendingActivePath ?? undefined" :handle-click="onNavClick"
|
||||
:handle-transition-navigation="handleMobileTransitionNavigation" :on-item-mouse-enter="() => { }"
|
||||
:on-item-mouse-leave="() => { }" :is-transitioning="isTransitioning" />
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="useful-links">
|
||||
<p class="useful-links-title">Useful Links</p>
|
||||
<div class="links-container">
|
||||
<a href="https://github.com/DavidHDev/vue-bits" target="_blank" @click="closeDrawer" class="useful-link">
|
||||
<span>GitHub</span>
|
||||
<i class="pi pi-arrow-up-right arrow-icon"></i>
|
||||
</a>
|
||||
<router-link to="/text-animations/split-text" @click="closeDrawer" class="useful-link">
|
||||
<span>Docs</span>
|
||||
<i class="pi pi-arrow-up-right arrow-icon"></i>
|
||||
</router-link>
|
||||
<a href="https://davidhaz.com/" target="_blank" @click="closeDrawer" class="useful-link">
|
||||
<span>Who made this?</span>
|
||||
<i class="pi pi-arrow-up-right arrow-icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Sidebar -->
|
||||
<nav ref="sidebarContainerRef" class="sidebar" :class="{ 'sidebar-no-fade': isScrolledToBottom }"
|
||||
@scroll="handleScroll">
|
||||
<div ref="sidebarRef" class="sidebar-content">
|
||||
<!-- Active line indicator -->
|
||||
<div class="active-line" :style="{
|
||||
transform: isLineVisible && linePosition !== null
|
||||
? `translateY(${linePosition - 8}px)`
|
||||
: 'translateY(-100px)',
|
||||
opacity: isLineVisible ? 1 : 0
|
||||
}"></div>
|
||||
|
||||
<!-- Hover line indicator -->
|
||||
<div class="hover-line" :style="{
|
||||
transform: hoverLinePosition !== null
|
||||
? `translateY(${hoverLinePosition - 8}px)`
|
||||
: 'translateY(-100px)',
|
||||
opacity: isHoverLineVisible ? 1 : 0
|
||||
}"></div>
|
||||
|
||||
<div class="categories-list">
|
||||
<Category v-for="cat in CATEGORIES" :key="cat.name" :category="cat" :location="route"
|
||||
:pending-active-path="pendingActivePath ?? undefined" :handle-click="scrollToTop"
|
||||
:handle-transition-navigation="handleTransitionNavigation" :on-item-mouse-enter="onItemEnter"
|
||||
:on-item-mouse-leave="onItemLeave" :is-transitioning="isTransitioning" />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, nextTick, watch, defineComponent, h, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { CATEGORIES, NEW, UPDATED } from '../../constants/Categories'
|
||||
import Logo from '../../assets/logos/vue-bits-logo.svg'
|
||||
import '../../css/sidebar.css'
|
||||
|
||||
const HOVER_TIMEOUT_DELAY = 150
|
||||
|
||||
// Reactive data
|
||||
const isDrawerOpen = ref(false)
|
||||
const linePosition = ref<number | null>(null)
|
||||
const isLineVisible = ref(false)
|
||||
const hoverLinePosition = ref<number | null>(null)
|
||||
const isHoverLineVisible = ref(false)
|
||||
const pendingActivePath = ref<string | null>(null)
|
||||
const isScrolledToBottom = ref(false)
|
||||
const isTransitioning = ref(false)
|
||||
|
||||
const sidebarRef = ref<HTMLDivElement>()
|
||||
const sidebarContainerRef = ref<HTMLDivElement>()
|
||||
|
||||
// Timeouts
|
||||
let hoverTimeoutRef: number | null = null
|
||||
let hoverDelayTimeoutRef: number | null = null
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// Helper functions
|
||||
const scrollToTop = () => window.scrollTo(0, 0)
|
||||
const slug = (str: string) => str.replace(/\s+/g, "-").toLowerCase()
|
||||
|
||||
const findActiveElement = () => {
|
||||
const activePath = pendingActivePath.value || route.path
|
||||
|
||||
for (const category of CATEGORIES) {
|
||||
const activeItem = category.subcategories.find((sub: string) => {
|
||||
const expectedPath = `/${slug(category.name)}/${slug(sub)}`
|
||||
return activePath === expectedPath
|
||||
})
|
||||
if (activeItem) {
|
||||
// Try to find the element within the sidebar
|
||||
const selector = `.sidebar a[href="${activePath}"]`
|
||||
const element = document.querySelector(selector) as HTMLElement
|
||||
return element
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const updateLinePosition = (el: HTMLElement | null) => {
|
||||
if (!el || !sidebarRef.value || !sidebarRef.value.offsetParent) return null
|
||||
const sidebarRect = sidebarRef.value.getBoundingClientRect()
|
||||
const elRect = el.getBoundingClientRect()
|
||||
return elRect.top - sidebarRect.top + elRect.height / 2
|
||||
}
|
||||
|
||||
const closeDrawer = () => {
|
||||
isDrawerOpen.value = false
|
||||
}
|
||||
|
||||
const onNavClick = () => {
|
||||
closeDrawer()
|
||||
scrollToTop()
|
||||
}
|
||||
|
||||
const handleTransitionNavigation = async (path: string) => {
|
||||
if (isTransitioning.value || route.path === path) return
|
||||
|
||||
pendingActivePath.value = path
|
||||
|
||||
// Simple navigation without transition for now
|
||||
// TODO: Implement transition when available
|
||||
await router.push(path)
|
||||
scrollToTop()
|
||||
pendingActivePath.value = null
|
||||
}
|
||||
|
||||
const handleMobileTransitionNavigation = async (path: string) => {
|
||||
if (isTransitioning.value || route.path === path) return
|
||||
|
||||
closeDrawer()
|
||||
pendingActivePath.value = path
|
||||
|
||||
// Simple navigation without transition for now
|
||||
// TODO: Implement transition when available
|
||||
await router.push(path)
|
||||
scrollToTop()
|
||||
pendingActivePath.value = null
|
||||
}
|
||||
|
||||
const onItemEnter = (path: string, e: Event) => {
|
||||
if (hoverTimeoutRef) clearTimeout(hoverTimeoutRef)
|
||||
if (hoverDelayTimeoutRef) clearTimeout(hoverDelayTimeoutRef)
|
||||
|
||||
const targetElement = e.currentTarget as HTMLElement
|
||||
const pos = updateLinePosition(targetElement)
|
||||
|
||||
if (pos !== null) {
|
||||
hoverLinePosition.value = pos
|
||||
}
|
||||
|
||||
hoverDelayTimeoutRef = setTimeout(() => {
|
||||
isHoverLineVisible.value = true
|
||||
}, 200)
|
||||
}
|
||||
|
||||
const onItemLeave = () => {
|
||||
if (hoverDelayTimeoutRef) clearTimeout(hoverDelayTimeoutRef)
|
||||
|
||||
hoverTimeoutRef = setTimeout(() => {
|
||||
isHoverLineVisible.value = false
|
||||
}, HOVER_TIMEOUT_DELAY)
|
||||
}
|
||||
|
||||
const handleScroll = () => {
|
||||
const sidebarElement = sidebarContainerRef.value
|
||||
if (!sidebarElement) return
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = sidebarElement
|
||||
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10
|
||||
isScrolledToBottom.value = isAtBottom
|
||||
}
|
||||
|
||||
const updateActiveLine = async () => {
|
||||
await nextTick()
|
||||
|
||||
// Wait a bit more to ensure DOM is fully updated
|
||||
setTimeout(() => {
|
||||
const activeEl = findActiveElement()
|
||||
|
||||
if (!activeEl) {
|
||||
isLineVisible.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const pos = updateLinePosition(activeEl)
|
||||
if (pos !== null) {
|
||||
linePosition.value = pos
|
||||
isLineVisible.value = true
|
||||
} else {
|
||||
isLineVisible.value = false
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// Category Component
|
||||
const Category = defineComponent({
|
||||
name: 'Category',
|
||||
props: {
|
||||
category: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
location: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
pendingActivePath: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
handleClick: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
handleTransitionNavigation: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
onItemMouseEnter: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
onItemMouseLeave: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
isTransitioning: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
interface ItemType {
|
||||
sub: string
|
||||
path: string
|
||||
isActive: boolean
|
||||
isNew: boolean
|
||||
isUpdated: boolean
|
||||
}
|
||||
|
||||
const items = computed(() =>
|
||||
props.category.subcategories.map((sub: string): ItemType => {
|
||||
const path = `/${slug(props.category.name)}/${slug(sub)}`
|
||||
const activePath = props.pendingActivePath || props.location.path
|
||||
return {
|
||||
sub,
|
||||
path,
|
||||
isActive: activePath === path,
|
||||
isNew: (NEW as string[]).includes(sub),
|
||||
isUpdated: (UPDATED as string[]).includes(sub),
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return () => h('div', { class: 'category' }, [
|
||||
h('p', { class: 'category-name' }, props.category.name),
|
||||
h('div', { class: 'category-items' },
|
||||
items.value.map(({ sub, path, isActive, isNew, isUpdated }: ItemType) => {
|
||||
return h('router-link', {
|
||||
key: path,
|
||||
to: path,
|
||||
class: [
|
||||
'sidebar-item',
|
||||
{ 'active-sidebar-item': isActive },
|
||||
{ 'transitioning': props.isTransitioning }
|
||||
],
|
||||
onClick: (e: Event) => {
|
||||
e.preventDefault()
|
||||
props.handleTransitionNavigation(path)
|
||||
},
|
||||
onMouseenter: (e: Event) => props.onItemMouseEnter(path, e),
|
||||
onMouseleave: props.onItemMouseLeave
|
||||
}, {
|
||||
default: () => [
|
||||
sub,
|
||||
isNew ? h('span', { class: 'new-tag' }, 'New') : null,
|
||||
isUpdated ? h('span', { class: 'updated-tag' }, 'Updated') : null
|
||||
].filter(Boolean)
|
||||
})
|
||||
})
|
||||
)
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
// Watchers
|
||||
watch(() => route.path, updateActiveLine)
|
||||
watch(pendingActivePath, updateActiveLine)
|
||||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
updateActiveLine()
|
||||
if (sidebarContainerRef.value) {
|
||||
sidebarContainerRef.value.addEventListener('scroll', handleScroll)
|
||||
handleScroll()
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (hoverTimeoutRef) clearTimeout(hoverTimeoutRef)
|
||||
if (hoverDelayTimeoutRef) clearTimeout(hoverDelayTimeoutRef)
|
||||
if (sidebarContainerRef.value) {
|
||||
sidebarContainerRef.value.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
18
src/composables/useForceRerender.ts
Normal file
18
src/composables/useForceRerender.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
/**
|
||||
* Composable for force re-rendering components
|
||||
* Useful for demo components that need to restart animations or reset state
|
||||
*/
|
||||
export function useForceRerender() {
|
||||
const rerenderKey = ref(0)
|
||||
|
||||
const forceRerender = () => {
|
||||
rerenderKey.value++
|
||||
}
|
||||
|
||||
return {
|
||||
rerenderKey,
|
||||
forceRerender
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Highlighted sidebar items
|
||||
export const NEW = [];
|
||||
export const UPDATED = [];
|
||||
|
||||
// Used for main sidebar navigation
|
||||
export const CATEGORIES = [
|
||||
{
|
||||
name: 'Text Animations',
|
||||
subcategories: [
|
||||
'Split Text',
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Animations',
|
||||
subcategories: [
|
||||
'Fade Content',
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Components',
|
||||
subcategories: [
|
||||
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Backgrounds',
|
||||
subcategories: [
|
||||
'Dot Grid',
|
||||
],
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,22 @@
|
||||
const animations = {
|
||||
'fade-content': () => import("../demo/Animations/FadeContentDemo.vue"),
|
||||
};
|
||||
|
||||
const textAnimations = {
|
||||
'split-text': () => import("../demo/TextAnimations/SplitTextDemo.vue"),
|
||||
};
|
||||
|
||||
const components = {
|
||||
|
||||
};
|
||||
|
||||
const backgrounds = {
|
||||
'dot-grid': () => import("../demo/Backgrounds/DotGridDemo.vue"),
|
||||
};
|
||||
|
||||
export const componentMap = {
|
||||
...animations,
|
||||
...textAnimations,
|
||||
...components,
|
||||
...backgrounds,
|
||||
};
|
||||
|
||||
27
src/constants/code/Animations/fadeContentCode.ts
Normal file
27
src/constants/code/Animations/fadeContentCode.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import code from '@content/Animations/FadeContent/FadeContent.vue?raw'
|
||||
import type { CodeObject } from '../../../types/code'
|
||||
|
||||
export const fadeContent: CodeObject = {
|
||||
cli: `npx jsrepo add https://vuebits.dev/Animations/FadeContent`,
|
||||
usage: `<template>
|
||||
<FadeContent
|
||||
:blur="true"
|
||||
:duration="1000"
|
||||
:delay="200"
|
||||
:threshold="0.1"
|
||||
:initial-opacity="0"
|
||||
easing="ease-out"
|
||||
class-name="my-fade-content"
|
||||
>
|
||||
<div class="content-to-fade">
|
||||
<h2>This content will fade in!</h2>
|
||||
<p>It appears when scrolled into view.</p>
|
||||
</div>
|
||||
</FadeContent>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FadeContent from "./FadeContent.vue";
|
||||
</script>`,
|
||||
code
|
||||
}
|
||||
39
src/constants/code/Backgrounds/dotGridCode.ts
Normal file
39
src/constants/code/Backgrounds/dotGridCode.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import code from '@content/Backgrounds/DotGrid/DotGrid.vue?raw'
|
||||
import type { CodeObject } from '../../../types/code'
|
||||
|
||||
export const dotGrid: CodeObject = {
|
||||
cli: `npx jsrepo add https://vuebits.dev/Backgrounds/DotGrid`,
|
||||
installation: `npm install gsap`,
|
||||
usage: `<template>
|
||||
<div class="dot-grid-container">
|
||||
<DotGrid
|
||||
:dot-size="16"
|
||||
:gap="32"
|
||||
base-color="#27FF64"
|
||||
active-color="#27FF64"
|
||||
:proximity="150"
|
||||
:speed-trigger="100"
|
||||
:shock-radius="250"
|
||||
:shock-strength="5"
|
||||
:max-speed="5000"
|
||||
:resistance="750"
|
||||
:return-duration="1.5"
|
||||
class-name="custom-dot-grid"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DotGrid from "./DotGrid.vue";
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dot-grid-container {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>`,
|
||||
code
|
||||
}
|
||||
33
src/constants/code/TextAnimations/splitTextCode.ts
Normal file
33
src/constants/code/TextAnimations/splitTextCode.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
// Fun fact: this is the first component ever made for Vue Bits!
|
||||
import code from '@content/TextAnimations/SplitText/SplitText.vue?raw'
|
||||
import type { CodeObject } from '../../../types/code'
|
||||
|
||||
export const splitText: CodeObject = {
|
||||
cli: `npx jsrepo add https://vuebits.dev/TextAnimations/SplitText`,
|
||||
installation: `npm install gsap`,
|
||||
usage: `<template>
|
||||
<SplitText
|
||||
text="Hello, GSAP!"
|
||||
class-name="text-2xl font-semibold text-center"
|
||||
:delay="100"
|
||||
:duration="0.6"
|
||||
ease="power3.out"
|
||||
split-type="chars"
|
||||
:from="{ opacity: 0, y: 40 }"
|
||||
:to="{ opacity: 1, y: 0 }"
|
||||
:threshold="0.1"
|
||||
root-margin="-100px"
|
||||
text-align="center"
|
||||
@animation-complete="handleAnimationComplete"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SplitText from "./SplitText.vue";
|
||||
|
||||
const handleAnimationComplete = () => {
|
||||
console.log('All letters have animated!');
|
||||
};
|
||||
</script>`,
|
||||
code
|
||||
}
|
||||
@@ -51,8 +51,8 @@ export interface DotGridProps {
|
||||
const props = withDefaults(defineProps<DotGridProps>(), {
|
||||
dotSize: 16,
|
||||
gap: 32,
|
||||
baseColor: '#5227FF',
|
||||
activeColor: '#5227FF',
|
||||
baseColor: '#27FF64',
|
||||
activeColor: '#27FF64',
|
||||
proximity: 150,
|
||||
speedTrigger: 100,
|
||||
shockRadius: 250,
|
||||
|
||||
@@ -84,7 +84,7 @@ const drawGrid = () => {
|
||||
Math.sqrt(canvas.width ** 2 + canvas.height ** 2) / 2
|
||||
)
|
||||
gradient.addColorStop(0, "rgba(0, 0, 0, 0)")
|
||||
gradient.addColorStop(1, "#0e0e0e")
|
||||
gradient.addColorStop(1, "#0b0b0b")
|
||||
|
||||
ctx.fillStyle = gradient
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||
|
||||
@@ -47,6 +47,10 @@ const props = withDefaults(defineProps<SplitTextProps>(), {
|
||||
textAlign: 'center'
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'animation-complete': []
|
||||
}>()
|
||||
|
||||
const textRef = ref<HTMLParagraphElement | null>(null)
|
||||
const animationCompletedRef = ref(false)
|
||||
const scrollTriggerRef = ref<ScrollTrigger | null>(null)
|
||||
@@ -130,6 +134,7 @@ const initializeAnimation = async () => {
|
||||
immediateRender: true,
|
||||
})
|
||||
props.onLetterAnimationComplete?.()
|
||||
emit('animation-complete')
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -8,10 +8,23 @@
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #0e0e0e;
|
||||
background-color: #0b0b0b;
|
||||
font-family: 'Figtree', sans-serif !important;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
min-height: 100vh;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
padding: 0 2em;
|
||||
}
|
||||
|
||||
.back-to-top {
|
||||
background: #0b0b0b !important;
|
||||
border: 1px solid #142216;
|
||||
}
|
||||
479
src/css/category.css
Normal file
479
src/css/category.css
Normal file
@@ -0,0 +1,479 @@
|
||||
.category-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main-nav {
|
||||
background: linear-gradient(to bottom, #0b0b0b, transparent);
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
padding: 0 2em;
|
||||
height: 85px;
|
||||
background: #0b0b0b;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.main-nav .nav-items {
|
||||
position: relative;
|
||||
height: 85px;
|
||||
padding: 0;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main-nav .nav-items::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right, transparent, #1e3726, transparent);
|
||||
}
|
||||
|
||||
.main-nav .nav-items::after {
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 70px;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background: linear-gradient(to bottom, #0b0b0b, transparent);
|
||||
}
|
||||
|
||||
.main-nav .nav-items .logo {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.main-nav .nav-items .logo img {
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
color: white;
|
||||
font-weight: 900;
|
||||
letter-spacing: -1px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.category-page {
|
||||
width: calc(100% - 200px);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: 8em 0 6em 1em;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.category-page .main-category {
|
||||
text-transform: uppercase;
|
||||
opacity: 0.5;
|
||||
letter-spacing: -0.5px;
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-page .sub-category {
|
||||
margin-bottom: 0.5em;
|
||||
line-height: 1em;
|
||||
font-size: 4.5rem;
|
||||
color: #fff;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
min-height: 500px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: #0b0b0b;
|
||||
border: 1px solid #142216;
|
||||
padding: 1em;
|
||||
margin-top: 1.4rem;
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
div:has(> .props-table) {
|
||||
border: 1px solid #142216;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.split-text-demo,
|
||||
.blur-text-demo {
|
||||
font-size: 6rem;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.scrambled-text-demo {
|
||||
max-width: 600px;
|
||||
font-size: 1rem;
|
||||
font-weight: bolder;
|
||||
color: #27FF64;
|
||||
}
|
||||
|
||||
.star-border-demo .inner-content {
|
||||
padding: 1em 3em;
|
||||
background-color: #0b0b0b !important;
|
||||
border: 1px solid #142216 !important
|
||||
}
|
||||
|
||||
.custom-bounceCards {
|
||||
position: relative;
|
||||
top: 2em;
|
||||
}
|
||||
|
||||
.custom-folder {
|
||||
margin-top: 4em;
|
||||
}
|
||||
|
||||
.rotating-text-demo-container {
|
||||
white-space: pre;
|
||||
font-size: clamp(2rem, 6vw, 6rem);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rotating-text-demo-container .rotating-text-demo {
|
||||
display: inline-block;
|
||||
color: #0b0b0b;
|
||||
background-color: #27FF64;
|
||||
padding: 0 0.4em;
|
||||
border-radius: 15px;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.ballpit-demo {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tilted-card-demo-text {
|
||||
font-family: "Figtree", sans-serif;
|
||||
font-weight: 900;
|
||||
text-transform: capitalize;
|
||||
box-shadow: 0 5px 30px #0b0b0b59;
|
||||
border-radius: 15px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 0.5rem 1em;
|
||||
letter-spacing: -0.5px;
|
||||
margin: 2em;
|
||||
}
|
||||
|
||||
.shapeblur-demo {
|
||||
position: relative;
|
||||
mix-blend-mode: difference;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.decrypted-text {
|
||||
font-size: 2rem;
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.variable-proximity-demo {
|
||||
max-width: 20ch;
|
||||
line-height: 100%;
|
||||
font-size: 4rem !important;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.shiny-text-demo {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.shiny-button {
|
||||
cursor: pointer;
|
||||
border: 1px solid #353535;
|
||||
background-color: #111;
|
||||
padding: 0.4em 1.2em;
|
||||
border-radius: 50px;
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
.shiny-button:hover {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
font-weight: 900;
|
||||
font-size: 1.6rem;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: .4rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.demo-title span {
|
||||
font-size: 0.8rem;
|
||||
color: #a1a1aa;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.demo-title::first-letter {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.demo-title-contribute {
|
||||
margin: 0;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.demo-title-contribute {
|
||||
letter-spacing: -0.5px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
font-size: 1.65rem;
|
||||
color: #A7EF9E;
|
||||
}
|
||||
|
||||
.contribute-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.contribute-button {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
background: #333333;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #142216;
|
||||
color: #fff;
|
||||
height: 2.5rem;
|
||||
padding: 0 1rem;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.contribute-button:hover {
|
||||
background: #142216;
|
||||
}
|
||||
|
||||
.contribute-separator {
|
||||
margin: 0 0.5rem;
|
||||
font-size: 16px;
|
||||
color: #a1a1aa;
|
||||
}
|
||||
|
||||
.contribute-container {
|
||||
background: #0b0b0b;
|
||||
border: 1px solid #142216;
|
||||
border-radius: 20px;
|
||||
padding: 46px;
|
||||
margin-top: 1.4rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.contribute-button {
|
||||
background-color: #0b0b0b !important;
|
||||
border: 1px solid #142216;
|
||||
transition: 0.5s ease;
|
||||
}
|
||||
|
||||
.contribute-button span {
|
||||
color: #a1a1aa;
|
||||
}
|
||||
|
||||
.contribute-button:hover {
|
||||
background-color: #142216 !important;
|
||||
}
|
||||
|
||||
.demo-details {
|
||||
font-size: 1.6rem;
|
||||
margin: 0 0 1em;
|
||||
display: flex;
|
||||
gap: 0.3em;
|
||||
}
|
||||
|
||||
.demo-details span {
|
||||
background-color: #111;
|
||||
border: 1px solid #142216;
|
||||
white-space: nowrap;
|
||||
color: #A7EF9E;
|
||||
border-radius: 10px;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.5em 1em;
|
||||
transition: 0.3s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-extra-info {
|
||||
margin: 1em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
color: #a1a1aa;
|
||||
}
|
||||
|
||||
.jsrepo-info a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cli-divider {
|
||||
position: relative;
|
||||
margin: 2em auto 2em;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right, transparent, #142216, transparent);
|
||||
}
|
||||
|
||||
.cli-divider::before {
|
||||
content: "Or";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #0b0b0b;
|
||||
padding: 0 1em;
|
||||
color: #a1a1aa;
|
||||
}
|
||||
|
||||
.coming-soon a {
|
||||
color: #27FF64;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.main-nav {
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
.category-wrapper {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.category-page {
|
||||
width: 100%;
|
||||
padding: 6em 1em 0;
|
||||
}
|
||||
|
||||
.category-page .main-category {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.category-page .sub-category {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.split-text-demo,
|
||||
.blur-text-demo {
|
||||
font-size: 2rem;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.decrypted-text {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.variable-proximity-demo {
|
||||
font-size: 1.6rem !important;
|
||||
}
|
||||
|
||||
.contribute-buttons {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.contribute-button {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.contribute-separator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.demo-title-contribute {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-gradient-class {
|
||||
padding: 1em 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.count-up-text {
|
||||
font-size: 4rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.custom-spotlight-card {
|
||||
user-select: none;
|
||||
background-color: #333333 !important;
|
||||
border: 1px solid #142216 !important;
|
||||
width: 350px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
/* Contribution Section Responsive Styles */
|
||||
.contribute-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.contribute-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
text-decoration: none;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.contribute-button i {
|
||||
font-size: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contribute-button span {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.contribute-separator {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.contribute-buttons {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contribute-button {
|
||||
width: 100%;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.contribute-separator {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
left: 0;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
background: linear-gradient(to right, #0e0e0e, transparent);
|
||||
background: linear-gradient(to right, #0b0b0b, transparent);
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
right: 0;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
background: linear-gradient(to left, #0e0e0e, transparent);
|
||||
background: linear-gradient(to left, #0b0b0b, transparent);
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import './base.css';
|
||||
@import './sidebar.css';
|
||||
@import './category.css';
|
||||
@import './landing.css';
|
||||
|
||||
|
||||
643
src/css/sidebar.css
Normal file
643
src/css/sidebar.css
Normal file
@@ -0,0 +1,643 @@
|
||||
.sidebar,
|
||||
.sidebar-mobile {
|
||||
background-color: #0b0b0b !important;
|
||||
}
|
||||
|
||||
.sidebar::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(to top, #0b0b0b, transparent);
|
||||
z-index: 4;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar.sidebar-no-fade::before {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 4.75rem 0 6em 0 !important;
|
||||
position: fixed;
|
||||
top: 57px;
|
||||
height: 100vh;
|
||||
width: 0;
|
||||
padding: 1.25rem;
|
||||
overflow-y: auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
display: block;
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-track {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background: #0b0b0b;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-thumb:hover {
|
||||
background: #142216;
|
||||
}
|
||||
|
||||
.sidebar-logo {
|
||||
padding: 2em calc(1em + 2px) !important;
|
||||
border-bottom: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar-logo img {
|
||||
position: relative;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.sidebar-item {
|
||||
position: relative;
|
||||
font-size: 0.9rem;
|
||||
color: #a1a1aa;
|
||||
padding: 0.25em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: color 0.3s ease;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.sidebar-item:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.active-sidebar-item {
|
||||
width: fit-content;
|
||||
padding: 0.25em 0.25em 0.25em 0;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.active-sidebar-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -1.15rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
background-color: #fff;
|
||||
border-radius: 1px;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.active-sidebar-item:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar-item .new-tag,
|
||||
.sidebar-item .updated-tag {
|
||||
margin-left: 0.6em;
|
||||
font-size: 10px;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
padding: 0.2em 0.4em;
|
||||
opacity: 1;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.sidebar-item .new-tag {
|
||||
color: #fff;
|
||||
border: 1px solid #5227ff;
|
||||
background-color: rgba(82, 39, 255, 0.3);
|
||||
}
|
||||
|
||||
.sidebar-item .updated-tag {
|
||||
color: #fff;
|
||||
border: 1px solid #ffffff77;
|
||||
background-color: #ffffff26;
|
||||
}
|
||||
|
||||
.github-button {
|
||||
border: 1px solid transparent;
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
.github-button img {
|
||||
transition: filter 0.3s ease;
|
||||
}
|
||||
|
||||
.github-button:hover {
|
||||
border-color: #ffffff1c;
|
||||
background-color: #0b0b0b !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.github-button:hover img {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.cta-button-docs {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
padding: 0 0 0 1rem;
|
||||
height: 40px;
|
||||
background: linear-gradient(135deg,
|
||||
rgb(30, 160, 63),
|
||||
rgba(24, 47, 255, 0.6));
|
||||
background-size: 200% 200%;
|
||||
backdrop-filter: blur(25px);
|
||||
-webkit-backdrop-filter: blur(25px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
justify-content: space-between;
|
||||
transition: .3s ease;
|
||||
}
|
||||
|
||||
.cta-button-docs span {
|
||||
background-color: #0b0b0b;
|
||||
margin-left: 1em;
|
||||
margin-right: calc(1em - 6px);
|
||||
padding-top: .1em;
|
||||
height: 30px;
|
||||
border-radius: 50px;
|
||||
width: 75px;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cta-button-docs span img {
|
||||
margin-right: 4px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
transition: .3s ease;
|
||||
}
|
||||
|
||||
/* Active and Hover Line Indicators */
|
||||
.active-line {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
background-color: #fff;
|
||||
border-radius: 1px;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.hover-line {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
background-color: #ffffff66;
|
||||
border-radius: 1px;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Categories Structure */
|
||||
.categories-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.category {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: -1px;
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.category-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.125rem;
|
||||
padding-left: 1rem;
|
||||
border-left: 2px solid #ffffff33;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mobile-logo {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.mobile-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
border-radius: 10px;
|
||||
border: 1px solid #ffffff1c;
|
||||
background: #0b0b0b;
|
||||
color: #fff;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
.icon-button:hover {
|
||||
border-color: #ffffff33;
|
||||
background-color: #0a0a0a;
|
||||
}
|
||||
|
||||
/* Header Component Styles */
|
||||
.main-nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background-color: #0b0b0b;
|
||||
}
|
||||
|
||||
.nav-items {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 80px;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.mobile-menu-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.5rem;
|
||||
background: #0b0b0b;
|
||||
border: 1px solid #ffffff1c;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.mobile-menu-button:hover {
|
||||
border-color: #ffffff33;
|
||||
background-color: #0a0a0a;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.mobile-menu-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-nav {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.desktop-nav {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.search-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
height: 40px;
|
||||
padding: 0 0.5rem 0 0.75rem;
|
||||
border-radius: 50px;
|
||||
background: #0b0b0b;
|
||||
border: 1px solid #142216;
|
||||
font-weight: 600;
|
||||
cursor: text;
|
||||
user-select: none;
|
||||
transition: transform 0.3s ease;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.search-button:hover {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
color: #142216;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.search-text {
|
||||
color: #a6a6a6;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.search-kbd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
font-size: 10px;
|
||||
padding: 0 0.5rem;
|
||||
border-radius: 50px;
|
||||
background: #1a1a1a;
|
||||
color: #a6a6a6;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.cta-button-docs {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
padding: 0 0 0 1rem;
|
||||
height: 40px;
|
||||
background: linear-gradient(135deg,
|
||||
rgb(30, 160, 63),
|
||||
rgba(24, 47, 255, 0.6));
|
||||
background-size: 200% 200%;
|
||||
backdrop-filter: blur(25px);
|
||||
-webkit-backdrop-filter: blur(25px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||
color: #fff;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
justify-content: space-between;
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
.cta-button-docs:hover {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.star-count {
|
||||
background-color: #0b0b0b;
|
||||
margin-left: 1em;
|
||||
margin-right: calc(1em - 6px);
|
||||
padding-top: 0.1em;
|
||||
height: 30px;
|
||||
border-radius: 50px;
|
||||
width: 75px;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.star-count img {
|
||||
margin-right: 4px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
/* Mobile Drawer Styles */
|
||||
.drawer-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1500;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #0b0b0b;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.drawer-header {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 85px;
|
||||
padding: 0 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.drawer-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right, transparent, #1e3726, transparent);
|
||||
}
|
||||
|
||||
.drawer-logo {
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.5rem;
|
||||
background: #0b0b0b;
|
||||
border: 1px solid #ffffff1c;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
border-color: #ffffff33;
|
||||
background-color: #0a0a0a;
|
||||
}
|
||||
|
||||
.drawer-body {
|
||||
padding: 0 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.drawer-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.drawer-link {
|
||||
color: #a1a1aa;
|
||||
text-decoration: none;
|
||||
padding: 0.25rem 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.drawer-link:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
transform: rotate(-45deg);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.drawer-separator {
|
||||
height: 1px;
|
||||
background-color: #ffffff1c;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
/* Drawer Navigation Styles */
|
||||
.drawer-navigation {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.drawer-navigation .categories-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.drawer-navigation .category {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.drawer-navigation .category-name {
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: -1px;
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.drawer-navigation .category-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.125rem;
|
||||
padding-left: 1rem;
|
||||
border-left: 2px solid #ffffff33;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.drawer-navigation .sidebar-item {
|
||||
position: relative;
|
||||
font-size: 0.9rem;
|
||||
color: #a1a1aa;
|
||||
padding: 0.25em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: color 0.3s ease;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.drawer-navigation .sidebar-item:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.drawer-navigation .active-sidebar-item {
|
||||
width: fit-content;
|
||||
padding: 0.25em 0.25em 0.25em 0;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.drawer-navigation .active-sidebar-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -1.15rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
background-color: #fff;
|
||||
border-radius: 1px;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.drawer-navigation .active-sidebar-item:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.drawer-navigation .sidebar-item .new-tag,
|
||||
.drawer-navigation .sidebar-item .updated-tag {
|
||||
margin-left: 0.6em;
|
||||
font-size: 10px;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
padding: 0.2em 0.4em;
|
||||
opacity: 1;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.drawer-navigation .sidebar-item .new-tag {
|
||||
color: #fff;
|
||||
border: 1px solid #5227ff;
|
||||
background-color: rgba(82, 39, 255, 0.3);
|
||||
}
|
||||
|
||||
.drawer-navigation .sidebar-item .updated-tag {
|
||||
color: #fff;
|
||||
border: 1px solid #ffffff77;
|
||||
background-color: #ffffff26;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.drawer-overlay {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.drawer-header {
|
||||
padding: 0 1em;
|
||||
}
|
||||
}
|
||||
117
src/demo/Animations/FadeContentDemo.vue
Normal file
117
src/demo/Animations/FadeContentDemo.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div class="fade-content-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<RefreshButton @refresh="forceRerender" />
|
||||
|
||||
<FadeContent
|
||||
:key="rerenderKey"
|
||||
:blur="blur"
|
||||
:duration="duration"
|
||||
:delay="delay"
|
||||
:threshold="threshold"
|
||||
:initial-opacity="initialOpacity"
|
||||
:easing="easing"
|
||||
class="fade-content-demo-content"
|
||||
>
|
||||
<div class="demo-content">
|
||||
<h4>Fade Content Example</h4>
|
||||
<p>This content will fade in when it enters the viewport.</p>
|
||||
</div>
|
||||
</FadeContent>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSwitch title="Enable Blur Effect" v-model="blur" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Duration (ms)" v-model="duration" :min="100" :max="3000" :step="100"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Delay (ms)" v-model="delay" :min="0" :max="1000" :step="50"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Threshold" v-model="threshold" :min="0.1" :max="1" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Initial Opacity" v-model="initialOpacity" :min="0" :max="1" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="fadeContent" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="fadeContent.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue'
|
||||
import RefreshButton from '../../components/common/RefreshButton.vue'
|
||||
import PropTable from '../../components/common/PropTable.vue'
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue'
|
||||
import CodeExample from '../../components/code/CodeExample.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import FadeContent from '../../content/Animations/FadeContent/FadeContent.vue'
|
||||
import { fadeContent } from '@/constants/code/Animations/fadeContentCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const blur = ref(true)
|
||||
const duration = ref(1000)
|
||||
const delay = ref(200)
|
||||
const threshold = ref(0.1)
|
||||
const initialOpacity = ref(0)
|
||||
const easing = ref('ease-out')
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const propData = [
|
||||
{ name: 'blur', type: 'boolean', default: 'false', description: 'Enable blur effect during fade animation.' },
|
||||
{ name: 'duration', type: 'number', default: '1000', description: 'Animation duration in milliseconds.' },
|
||||
{ name: 'easing', type: 'string', default: '"ease-out"', description: 'CSS easing function for the animation.' },
|
||||
{ name: 'delay', type: 'number', default: '0', description: 'Delay before animation starts in milliseconds.' },
|
||||
{ name: 'threshold', type: 'number', default: '0.1', description: 'Intersection threshold to trigger animation (0-1).' },
|
||||
{ name: 'initialOpacity', type: 'number', default: '0', description: 'Initial opacity before animation (0-1).' },
|
||||
{ name: 'className', type: 'string', default: '""', description: 'Additional CSS classes for styling.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fade-content-demo-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.demo-content {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
border: 1px solid #ffffff1c;
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.demo-content h4 {
|
||||
color: #fff;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-content p {
|
||||
color: #a1a1aa;
|
||||
text-align: center;
|
||||
max-width: 25ch;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
153
src/demo/Backgrounds/DotGridDemo.vue
Normal file
153
src/demo/Backgrounds/DotGridDemo.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="dot-grid-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container" style="height: 500px; overflow: hidden;">
|
||||
<RefreshButton @refresh="forceRerender" />
|
||||
|
||||
<DotGrid :key="rerenderKey" :dot-size="dotSize" :gap="gap" :base-color="baseColor" :active-color="activeColor"
|
||||
:proximity="proximity" :speed-trigger="speedTrigger" :shock-radius="shockRadius"
|
||||
:shock-strength="shockStrength" :max-speed="maxSpeed" :resistance="resistance"
|
||||
:return-duration="returnDuration" class-name="dot-grid-demo-canvas" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<div class="color-controls">
|
||||
<div class="color-input">
|
||||
<label>Base Color</label>
|
||||
<input type="color" v-model="baseColor" @change="forceRerender" />
|
||||
</div>
|
||||
<div class="color-input">
|
||||
<label>Active Color</label>
|
||||
<input type="color" v-model="activeColor" @change="forceRerender" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PreviewSlider title="Dot Size" v-model="dotSize" :min="2" :max="50" :step="1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Gap" v-model="gap" :min="5" :max="100" :step="1" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Proximity" v-model="proximity" :min="50" :max="500" :step="10"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Speed Trigger" v-model="speedTrigger" :min="50" :max="500" :step="10"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Shock Radius" v-model="shockRadius" :min="50" :max="500" :step="10"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Shock Strength" v-model="shockStrength" :min="1" :max="20" :step="1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Max Speed" v-model="maxSpeed" :min="1000" :max="10000" :step="100"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Resistance (Inertia)" v-model="resistance" :min="100" :max="2000" :step="50"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Return Duration (Inertia)" v-model="returnDuration" :min="0.1" :max="5" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="dotGrid" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="dotGrid.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue'
|
||||
import RefreshButton from '../../components/common/RefreshButton.vue'
|
||||
import PropTable from '../../components/common/PropTable.vue'
|
||||
import Dependencies from '../../components/code/Dependencies.vue'
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue'
|
||||
import CodeExample from '../../components/code/CodeExample.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import DotGrid from '../../content/Backgrounds/DotGrid/DotGrid.vue'
|
||||
import { dotGrid } from '@/constants/code/Backgrounds/dotGridCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const dotSize = ref(5)
|
||||
const gap = ref(15)
|
||||
const baseColor = ref('#333333')
|
||||
const activeColor = ref('#27FF64')
|
||||
const proximity = ref(120)
|
||||
const speedTrigger = ref(100)
|
||||
const shockRadius = ref(250)
|
||||
const shockStrength = ref(5)
|
||||
const maxSpeed = ref(5000)
|
||||
const resistance = ref(750)
|
||||
const returnDuration = ref(1.5)
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const propData = [
|
||||
{ name: 'dotSize', type: 'number', default: '16', description: 'Size of each dot in pixels.' },
|
||||
{ name: 'gap', type: 'number', default: '32', description: 'Gap between each dot in pixels.' },
|
||||
{ name: 'baseColor', type: 'string', default: "'#27FF64'", description: 'Base color of the dots.' },
|
||||
{ name: 'activeColor', type: 'string', default: "'#27FF64'", description: 'Color of dots when hovered or activated.' },
|
||||
{ name: 'proximity', type: 'number', default: '150', description: 'Radius around the mouse pointer within which dots react.' },
|
||||
{ name: 'speedTrigger', type: 'number', default: '100', description: 'Mouse speed threshold to trigger inertia effect.' },
|
||||
{ name: 'shockRadius', type: 'number', default: '250', description: 'Radius of the shockwave effect on click.' },
|
||||
{ name: 'shockStrength', type: 'number', default: '5', description: 'Strength of the shockwave effect on click.' },
|
||||
{ name: 'maxSpeed', type: 'number', default: '5000', description: 'Maximum speed for inertia calculation.' },
|
||||
{ name: 'resistance', type: 'number', default: '750', description: 'Resistance for the inertia effect.' },
|
||||
{ name: 'returnDuration', type: 'number', default: '1.5', description: 'Duration for dots to return to their original position after inertia.' },
|
||||
{ name: 'className', type: 'string', default: "''", description: 'Additional CSS classes for the component.' },
|
||||
{ name: 'style', type: 'object', default: '{}', description: 'Inline styles for the component.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dot-grid-demo-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.color-controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.color-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.color-input label {
|
||||
font-size: 0.875rem;
|
||||
color: #a1a1aa;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.color-input input[type="color"] {
|
||||
width: 50px;
|
||||
height: 32px;
|
||||
border: 1px solid #333;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.color-input input[type="color"]::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.color-input input[type="color"]::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
88
src/demo/TextAnimations/SplitTextDemo.vue
Normal file
88
src/demo/TextAnimations/SplitTextDemo.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="split-text-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<RefreshButton @refresh="forceRerender" />
|
||||
|
||||
<SplitText :key="rerenderKey" text="Hello, Vue!" :delay="delay" :duration="duration" :ease="ease"
|
||||
:split-type="splitType" :threshold="threshold" class="split-text-demo-text"
|
||||
@animation-complete="() => { showCallback && showToast() }" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSwitch title="Show Completion Toast" v-model="showCallback" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Stagger Delay (ms)" v-model="delay" :min="10" :max="500" :step="10"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Duration (s)" v-model="duration" :min="0.1" :max="2" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Threshold" v-model="threshold" :min="0.1" :max="1" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="splitText" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="splitText.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue'
|
||||
import RefreshButton from '../../components/common/RefreshButton.vue'
|
||||
import PropTable from '../../components/common/PropTable.vue'
|
||||
import Dependencies from '../../components/code/Dependencies.vue'
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue'
|
||||
import CodeExample from '../../components/code/CodeExample.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import SplitText from '../../content/TextAnimations/SplitText/SplitText.vue'
|
||||
import { splitText } from '@/constants/code/TextAnimations/splitTextCode'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const delay = ref(10)
|
||||
const duration = ref(2)
|
||||
const ease = ref('elastic.out(1, 0.3)')
|
||||
const splitType = ref<'chars' | 'words' | 'lines' | 'words, chars'>('chars')
|
||||
const threshold = ref(0.1)
|
||||
const showCallback = ref(true)
|
||||
const toast = useToast()
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const showToast = () => {
|
||||
toast.add({
|
||||
severity: 'secondary',
|
||||
summary: 'Animation Finished!',
|
||||
life: 3000
|
||||
})
|
||||
}
|
||||
|
||||
const propData = [
|
||||
{ name: 'text', type: 'string', default: '""', description: 'The text content to animate.' },
|
||||
{ name: 'className', type: 'string', default: '""', description: 'Additional class names to style the component.' },
|
||||
{ name: 'delay', type: 'number', default: '100', description: 'Delay between animations for each letter (in ms).' },
|
||||
{ name: 'duration', type: 'number', default: '0.6', description: 'Duration of each letter animation (in seconds).' },
|
||||
{ name: 'ease', type: 'string', default: '"power3.out"', description: 'GSAP easing function for the animation.' },
|
||||
{ name: 'splitType', type: 'string', default: '"chars"', description: 'Split type: "chars", "words", "lines", or "words, chars".' },
|
||||
{ name: 'from', type: 'object', default: '{ opacity: 0, y: 40 }', description: 'Initial GSAP properties for each letter/word.' },
|
||||
{ name: 'to', type: 'object', default: '{ opacity: 1, y: 0 }', description: 'Target GSAP properties for each letter/word.' },
|
||||
{ name: 'threshold', type: 'number', default: '0.1', description: 'Intersection threshold to trigger the animation (0-1).' },
|
||||
{ name: 'rootMargin', type: 'string', default: '"-100px"', description: 'Root margin for the ScrollTrigger.' },
|
||||
{ name: 'textAlign', type: 'string', default: '"center"', description: 'Text alignment: "left", "center", "right", etc.' },
|
||||
{ name: 'onLetterAnimationComplete', type: 'function', default: 'undefined', description: 'Callback function when all animations complete.' }
|
||||
]
|
||||
</script>
|
||||
21
src/main.ts
21
src/main.ts
@@ -1,11 +1,32 @@
|
||||
/* eslint-disable vue/multi-word-component-names */
|
||||
/* eslint-disable vue/no-reserved-component-names */
|
||||
|
||||
import './css/main.css'
|
||||
import 'primeicons/primeicons.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
// PrimeVue imports
|
||||
import PrimeVue from 'primevue/config'
|
||||
import Aura from '@primeuix/themes/aura'
|
||||
import Button from 'primevue/button'
|
||||
import Toast from 'primevue/toast'
|
||||
import ToastService from 'primevue/toastservice'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: Aura
|
||||
}
|
||||
})
|
||||
app.use(ToastService)
|
||||
|
||||
// Global components
|
||||
app.component('Button', Button)
|
||||
app.component('Toast', Toast)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
@@ -1,11 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Category Page</h1>
|
||||
<p>Category: {{ $route.params.category }}</p>
|
||||
<p>Subcategory: {{ $route.params.subcategory }}</p>
|
||||
<div ref="scrollRef">
|
||||
<div class="page-transition-fade">
|
||||
<h2 class="sub-category">{{ decodedLabel }}</h2>
|
||||
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<component :is="SubcategoryComponent" v-if="SubcategoryComponent" />
|
||||
</template>
|
||||
<template #fallback>
|
||||
<div class="loading-placeholder">Loading...</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Add BackToTopButton component when available -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, nextTick, defineAsyncComponent } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { componentMap } from '../constants/Components'
|
||||
import { decodeLabel } from '../utils/utils'
|
||||
import '../css/category.css'
|
||||
|
||||
const route = useRoute()
|
||||
const scrollRef = ref<HTMLDivElement | null>(null)
|
||||
|
||||
const subcategory = computed(() => route.params.subcategory as string)
|
||||
const decodedLabel = computed(() => decodeLabel(subcategory.value))
|
||||
|
||||
// Lazy load the component based on subcategory
|
||||
const SubcategoryComponent = computed(() => {
|
||||
if (!subcategory.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
const componentLoader = componentMap[subcategory.value as keyof typeof componentMap]
|
||||
|
||||
if (!componentLoader) {
|
||||
return null
|
||||
}
|
||||
|
||||
return defineAsyncComponent(componentLoader)
|
||||
})
|
||||
|
||||
// Update document title
|
||||
watch(decodedLabel, (newLabel) => {
|
||||
if (newLabel) {
|
||||
document.title = `Vue Bits - ${newLabel}`
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// Scroll to top when subcategory changes
|
||||
watch(subcategory, async () => {
|
||||
if (scrollRef.value) {
|
||||
await nextTick()
|
||||
scrollRef.value.scrollTo(0, 0)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// Initial scroll to top
|
||||
if (scrollRef.value) {
|
||||
scrollRef.value.scrollTo(0, 0)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
7
src/types/code.ts
Normal file
7
src/types/code.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface CodeObject {
|
||||
cli?: string
|
||||
utility?: string
|
||||
code?: string
|
||||
usage?: string
|
||||
installation?: string
|
||||
}
|
||||
@@ -18,3 +18,17 @@ export const getStarsCount = async (repo: string = 'DavidHDev/vue-bits'): Promis
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a label from kebab-case to title case
|
||||
* @param label - The label to decode (e.g., "split-text")
|
||||
* @returns The decoded label (e.g., "Split Text")
|
||||
*/
|
||||
export const decodeLabel = (label: string): string => {
|
||||
if (!label) return ''
|
||||
|
||||
return label
|
||||
.split('-')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
@@ -13,7 +15,8 @@ export default defineConfig({
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
'@content': path.resolve(__dirname, 'src/content'),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user