feat: InspiraUI
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
// https://oxc.rs/docs/guide/usage/linter/rules/unicorn/no-new-array.html
|
||||
// "rules": {
|
||||
// "unicorn/no-new-array": "warn"
|
||||
// },
|
||||
"rules": {
|
||||
"unicorn/no-useless-spread": "off"
|
||||
},
|
||||
"ignorePatterns": ["src/shadcn/**", "src/components/InspiraUI/**"]
|
||||
}
|
||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -3,9 +3,9 @@
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit",
|
||||
"source.fixAll.oxc": "explicit",
|
||||
"source.fixAll.eslint": "never",
|
||||
"source.fixAll.stylelint": "never",
|
||||
"source.fixAll.oxc": "never",
|
||||
// "source.fixAll.eslint": "never",
|
||||
// "source.fixAll.stylelint": "never",
|
||||
// "source.fixAll.oxc": "never",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
"editor.formatOnSave": true,
|
||||
|
@ -89,6 +89,8 @@
|
||||
"@eslint/compat": "^1.2.7",
|
||||
"@faker-js/faker": "^9.6.0",
|
||||
"@iconify-json/carbon": "^1.2.8",
|
||||
"@iconify-json/logos": "^1.2.4",
|
||||
"@iconify-json/mdi": "^1.2.3",
|
||||
"@iconify/utils": "^2.3.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@primevue/auto-import-resolver": "^4.3.3",
|
||||
|
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
@ -149,6 +149,12 @@ importers:
|
||||
'@iconify-json/carbon':
|
||||
specifier: ^1.2.8
|
||||
version: 1.2.8
|
||||
'@iconify-json/logos':
|
||||
specifier: ^1.2.4
|
||||
version: 1.2.4
|
||||
'@iconify-json/mdi':
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3
|
||||
'@iconify/utils':
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
@ -915,6 +921,12 @@ packages:
|
||||
'@iconify-json/carbon@1.2.8':
|
||||
resolution: {integrity: sha512-6xh4YiFBz6qoSnB3XMe23WvjTJroDFXB17J1MbiT7nATFe+70+em1acRXr8hgP/gYpwFMHFc4IvjA/IPTPnTzg==}
|
||||
|
||||
'@iconify-json/logos@1.2.4':
|
||||
resolution: {integrity: sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw==}
|
||||
|
||||
'@iconify-json/mdi@1.2.3':
|
||||
resolution: {integrity: sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==}
|
||||
|
||||
'@iconify/types@2.0.0':
|
||||
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
|
||||
|
||||
@ -5314,6 +5326,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
'@iconify-json/logos@1.2.4':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
'@iconify-json/mdi@1.2.3':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
'@iconify/types@2.0.0': {}
|
||||
|
||||
'@iconify/utils@2.3.0':
|
||||
|
@ -20,7 +20,6 @@ const themeConfig = computed(() => {
|
||||
</a-config-provider>
|
||||
|
||||
<DynamicDialog /> <ConfirmDialog /> <Toast />
|
||||
<FluidCursor />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
@ -1,22 +1,39 @@
|
||||
<template>
|
||||
<div
|
||||
ref="bubbleParentContainer"
|
||||
class="relative h-72 w-full overflow-hidden"
|
||||
>
|
||||
<div ref="bubbleCanvasContainer"></div>
|
||||
<div
|
||||
:style="{
|
||||
'--bubbles-blur': `${blur}px`,
|
||||
}"
|
||||
class="absolute inset-0 z-[2] size-full backdrop-blur-[--bubbles-blur]"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Clock,
|
||||
Color,
|
||||
MathUtils,
|
||||
Mesh,
|
||||
PerspectiveCamera,
|
||||
Scene,
|
||||
ShaderMaterial,
|
||||
SphereGeometry,
|
||||
Vector3,
|
||||
Color,
|
||||
MathUtils,
|
||||
Mesh,
|
||||
Clock,
|
||||
WebGLRenderer,
|
||||
} from 'three';
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
Scene,
|
||||
PerspectiveCamera,
|
||||
} from "three";
|
||||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
||||
|
||||
defineProps({
|
||||
blur: {
|
||||
default: 0,
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@ -42,14 +59,16 @@ const SPHERE_COUNT = 250;
|
||||
const SPHERE_SCALE_COEFF = 3;
|
||||
const ORBIT_MIN = SPHERE_SCALE_COEFF + 2;
|
||||
const ORBIT_MAX = ORBIT_MIN + 10;
|
||||
const RAND_SEED = 898_211_544;
|
||||
const RAND_SEED = 898211544;
|
||||
|
||||
const rand = seededRandom(RAND_SEED);
|
||||
|
||||
const { cos, PI, sin } = Math;
|
||||
const { PI, cos, sin } = Math;
|
||||
const PI2 = PI * 2;
|
||||
const sizes = new Array(SPHERE_COUNT).fill(0).map(() => randRange(1) * Math.pow(randRange(), 3));
|
||||
const orbitRadii = new Array(SPHERE_COUNT).fill(0).map(() => MathUtils.lerp(ORBIT_MIN, ORBIT_MAX, randRange()));
|
||||
const orbitRadii = new Array(SPHERE_COUNT)
|
||||
.fill(0)
|
||||
.map(() => MathUtils.lerp(ORBIT_MIN, ORBIT_MAX, randRange()));
|
||||
const thetas = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
|
||||
const phis = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
|
||||
const positions: [number, number, number][] = orbitRadii.map((rad, i) => [
|
||||
@ -76,28 +95,90 @@ const bgMaterial = getGradientMaterial(
|
||||
);
|
||||
bgMaterial.uniforms.uTemperatureVariancePeriod.value = new Vector3(0, 0, 0.1);
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
function seededRandom(a: number) {
|
||||
return function () {
|
||||
a |= 0;
|
||||
a = (a + 0x9e3779b9) | 0;
|
||||
var t = a ^ (a >>> 16);
|
||||
t = Math.imul(t, 0x21f0aaad);
|
||||
t = t ^ (t >>> 15);
|
||||
t = Math.imul(t, 0x735a2d97);
|
||||
return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
const elapsed = clock.getElapsedTime();
|
||||
const temperature = sin(elapsed * 0.5) * 0.5 + 0.5;
|
||||
function randRange(n = 1) {
|
||||
return rand() * n;
|
||||
}
|
||||
|
||||
bgMaterial.uniforms.uTemperature.value = temperature;
|
||||
bgMaterial.uniforms.uElapsedTime.value = elapsed;
|
||||
function rgb(r: number, g: number, b: number) {
|
||||
return new Color(r / 255, g / 255, b / 255);
|
||||
}
|
||||
|
||||
sphereMaterial.uniforms.uTemperature.value = temperature;
|
||||
sphereMaterial.uniforms.uElapsedTime.value = elapsed;
|
||||
function getGradientMaterial(
|
||||
colorBottomWarm: Color,
|
||||
colorTopWarm: Color,
|
||||
colorBottomCool: Color,
|
||||
colorTopCool: Color,
|
||||
) {
|
||||
return new ShaderMaterial({
|
||||
uniforms: {
|
||||
colorBottomWarm: {
|
||||
value: new Color().copy(colorBottomWarm),
|
||||
},
|
||||
colorTopWarm: {
|
||||
value: new Color().copy(colorTopWarm),
|
||||
},
|
||||
colorBottomCool: {
|
||||
value: new Color().copy(colorBottomCool),
|
||||
},
|
||||
colorTopCool: {
|
||||
value: new Color().copy(colorTopCool),
|
||||
},
|
||||
uTemperature: {
|
||||
value: 0.0,
|
||||
},
|
||||
uTemperatureVariancePeriod: {
|
||||
value: new Vector3(0.08, 0.1, 0.2),
|
||||
},
|
||||
uElapsedTime: {
|
||||
value: 0,
|
||||
},
|
||||
},
|
||||
vertexShader: `
|
||||
uniform vec4 uTemperatureVariancePeriod;
|
||||
uniform float uTemperature;
|
||||
uniform float uElapsedTime;
|
||||
varying float topBottomMix;
|
||||
varying float warmCoolMix;
|
||||
|
||||
// Floating effect for spheres
|
||||
for (const [index, sphere] of spheres.entries()) {
|
||||
const basePosition = positions[index];
|
||||
const floatFactor = 2; // Adjust this value to control float intensity
|
||||
const speed = 0.3; // Adjust this value to control float speed
|
||||
const floatY = sin(elapsed * speed + index) * floatFactor;
|
||||
sphere.position.y = basePosition[1] + floatY;
|
||||
}
|
||||
void main() {
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
|
||||
topBottomMix = normal.y;
|
||||
warmCoolMix = 0.6 * uTemperature +
|
||||
0.4 * (sin(
|
||||
(uElapsedTime + gl_Position.x) * uTemperatureVariancePeriod.x +
|
||||
(uElapsedTime + gl_Position.y) * uTemperatureVariancePeriod.y +
|
||||
(uElapsedTime + gl_Position.z) * uTemperatureVariancePeriod.z) * 0.5 + 0.5);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
uniform vec3 colorBottomWarm;
|
||||
uniform vec3 colorTopWarm;
|
||||
uniform vec3 colorBottomCool;
|
||||
uniform vec3 colorTopCool;
|
||||
|
||||
renderer.render(scene, camera);
|
||||
varying float topBottomMix;
|
||||
varying float warmCoolMix;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(mix(
|
||||
mix(colorTopCool, colorTopWarm, warmCoolMix),
|
||||
mix(colorBottomCool, colorBottomWarm, warmCoolMix),
|
||||
topBottomMix), 1.0);
|
||||
}
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
function createScene() {
|
||||
@ -119,7 +200,7 @@ function createScene() {
|
||||
sphereMaterial.depthTest = true; // Keep this true for depth sorting
|
||||
|
||||
if (bubbleCanvasContainer.value) {
|
||||
bubbleCanvasContainer.value.append(renderer.domElement);
|
||||
bubbleCanvasContainer.value.appendChild(renderer.domElement);
|
||||
}
|
||||
|
||||
// Create the background mesh
|
||||
@ -138,12 +219,18 @@ function createScene() {
|
||||
const frustumWidth = frustumHeight * aspect;
|
||||
|
||||
// Scale the background geometry to match the camera's frustum size
|
||||
bgMesh.scale.set(frustumWidth / bgGeometry.parameters.radius, frustumHeight / bgGeometry.parameters.radius, 1);
|
||||
bgMesh.scale.set(
|
||||
frustumWidth / bgGeometry.parameters.radius,
|
||||
frustumHeight / bgGeometry.parameters.radius,
|
||||
1,
|
||||
);
|
||||
|
||||
scene.add(bgMesh); // Add the backgrou
|
||||
|
||||
// Create sphere meshes
|
||||
const orbitRadii = new Array(SPHERE_COUNT).fill(0).map(() => MathUtils.lerp(ORBIT_MIN, ORBIT_MAX, randRange()));
|
||||
const orbitRadii = new Array(SPHERE_COUNT)
|
||||
.fill(0)
|
||||
.map(() => MathUtils.lerp(ORBIT_MIN, ORBIT_MAX, randRange()));
|
||||
const thetas = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
|
||||
const phis = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
|
||||
const positions = orbitRadii.map((rad, i) => [
|
||||
@ -165,85 +252,28 @@ function createScene() {
|
||||
clock = new Clock();
|
||||
}
|
||||
|
||||
function getGradientMaterial(colorBottomWarm: Color, colorTopWarm: Color, colorBottomCool: Color, colorTopCool: Color) {
|
||||
return new ShaderMaterial({
|
||||
fragmentShader: `
|
||||
uniform vec3 colorBottomWarm;
|
||||
uniform vec3 colorTopWarm;
|
||||
uniform vec3 colorBottomCool;
|
||||
uniform vec3 colorTopCool;
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
varying float topBottomMix;
|
||||
varying float warmCoolMix;
|
||||
const elapsed = clock.getElapsedTime();
|
||||
const temperature = sin(elapsed * 0.5) * 0.5 + 0.5;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(mix(
|
||||
mix(colorTopCool, colorTopWarm, warmCoolMix),
|
||||
mix(colorBottomCool, colorBottomWarm, warmCoolMix),
|
||||
topBottomMix), 1.0);
|
||||
}
|
||||
`,
|
||||
uniforms: {
|
||||
colorBottomCool: {
|
||||
value: new Color().copy(colorBottomCool),
|
||||
},
|
||||
colorBottomWarm: {
|
||||
value: new Color().copy(colorBottomWarm),
|
||||
},
|
||||
colorTopCool: {
|
||||
value: new Color().copy(colorTopCool),
|
||||
},
|
||||
colorTopWarm: {
|
||||
value: new Color().copy(colorTopWarm),
|
||||
},
|
||||
uElapsedTime: {
|
||||
value: 0,
|
||||
},
|
||||
uTemperature: {
|
||||
value: 0,
|
||||
},
|
||||
uTemperatureVariancePeriod: {
|
||||
value: new Vector3(0.08, 0.1, 0.2),
|
||||
},
|
||||
},
|
||||
vertexShader: `
|
||||
uniform vec4 uTemperatureVariancePeriod;
|
||||
uniform float uTemperature;
|
||||
uniform float uElapsedTime;
|
||||
varying float topBottomMix;
|
||||
varying float warmCoolMix;
|
||||
bgMaterial.uniforms.uTemperature.value = temperature;
|
||||
bgMaterial.uniforms.uElapsedTime.value = elapsed;
|
||||
|
||||
void main() {
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
|
||||
topBottomMix = normal.y;
|
||||
warmCoolMix = 0.6 * uTemperature +
|
||||
0.4 * (sin(
|
||||
(uElapsedTime + gl_Position.x) * uTemperatureVariancePeriod.x +
|
||||
(uElapsedTime + gl_Position.y) * uTemperatureVariancePeriod.y +
|
||||
(uElapsedTime + gl_Position.z) * uTemperatureVariancePeriod.z) * 0.5 + 0.5);
|
||||
}
|
||||
`,
|
||||
sphereMaterial.uniforms.uTemperature.value = temperature;
|
||||
sphereMaterial.uniforms.uElapsedTime.value = elapsed;
|
||||
|
||||
// Floating effect for spheres
|
||||
spheres.forEach((sphere, index) => {
|
||||
const basePosition = positions[index];
|
||||
const floatFactor = 2; // Adjust this value to control float intensity
|
||||
const speed = 0.3; // Adjust this value to control float speed
|
||||
const floatY = sin(elapsed * speed + index) * floatFactor;
|
||||
sphere.position.y = basePosition[1] + floatY;
|
||||
});
|
||||
}
|
||||
|
||||
function randRange(n = 1) {
|
||||
return rand() * n;
|
||||
}
|
||||
|
||||
function rgb(r: number, g: number, b: number) {
|
||||
return new Color(r / 255, g / 255, b / 255);
|
||||
}
|
||||
|
||||
function seededRandom(a: number) {
|
||||
return function () {
|
||||
a = Math.trunc(a);
|
||||
a = Math.trunc(a + 0x9e_37_79_b9);
|
||||
let t = a ^ (a >>> 16);
|
||||
t = Math.imul(t, 0x21_f0_aa_ad);
|
||||
t = t ^ (t >>> 15);
|
||||
t = Math.imul(t, 0x73_5a_2d_97);
|
||||
return ((t = t ^ (t >>> 15)) >>> 0) / 4_294_967_296;
|
||||
};
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
function updateRendererSize() {
|
||||
@ -261,34 +291,26 @@ function updateRendererSize() {
|
||||
const frustumWidth = frustumHeight * camera.aspect;
|
||||
|
||||
// Get the background mesh and update its scale
|
||||
const bgMesh = scene.children.find((obj) => obj instanceof Mesh && obj.geometry === bgGeometry) as Mesh;
|
||||
const bgMesh = scene.children.find(
|
||||
(obj) => obj instanceof Mesh && obj.geometry === bgGeometry,
|
||||
) as Mesh;
|
||||
if (bgMesh) {
|
||||
bgMesh.scale.set(frustumWidth / bgGeometry.parameters.radius, frustumHeight / bgGeometry.parameters.radius, 1);
|
||||
bgMesh.scale.set(
|
||||
frustumWidth / bgGeometry.parameters.radius,
|
||||
frustumHeight / bgGeometry.parameters.radius,
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
createScene();
|
||||
updateRendererSize();
|
||||
window.addEventListener('resize', updateRendererSize);
|
||||
window.addEventListener("resize", updateRendererSize);
|
||||
animate();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateRendererSize); // Cleanup on component unmount
|
||||
window.removeEventListener("resize", updateRendererSize); // Cleanup on component unmount
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="bubbleParentContainer" class="relative h-72 w-full overflow-hidden">
|
||||
<div ref="bubbleCanvasContainer"></div>
|
||||
<div
|
||||
:style="{
|
||||
'--bubbles-blur': `${blur}px`,
|
||||
}"
|
||||
class="absolute inset-0 z-[2] size-full backdrop-blur-[--bubbles-blur]"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
1
src/components/InspiraUI/bg-bubbles/index.ts
Normal file
1
src/components/InspiraUI/bg-bubbles/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as BubblesBg } from "./BubblesBg.vue";
|
@ -1,22 +1,29 @@
|
||||
<template>
|
||||
<canvas
|
||||
ref="starsCanvas"
|
||||
:class="cn('absolute inset-0 w-full h-full', $props.class)"
|
||||
></canvas>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/shadcn/lib/utils';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { cn } from "@/shadcn/lib/utils";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
interface Star {
|
||||
speed: number;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
speed: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
class?: string;
|
||||
color?: string;
|
||||
count?: number;
|
||||
class?: string;
|
||||
}>(),
|
||||
{
|
||||
color: '#FFF',
|
||||
color: "#FFF",
|
||||
count: 200,
|
||||
},
|
||||
);
|
||||
@ -30,7 +37,7 @@ onMounted(() => {
|
||||
const canvas = starsCanvas.value;
|
||||
if (!canvas) return;
|
||||
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
window.addEventListener("resize", resizeCanvas);
|
||||
resizeCanvas(); // Call it initially to set correct size
|
||||
|
||||
perspective = canvas.width / 2;
|
||||
@ -39,41 +46,39 @@ onMounted(() => {
|
||||
// Initialize stars
|
||||
for (let i = 0; i < props.count; i++) {
|
||||
stars.push({
|
||||
speed: Math.random() * 5 + 2, // Speed for falling effect
|
||||
x: (Math.random() - 0.5) * 2 * canvas.width,
|
||||
y: (Math.random() - 0.5) * 2 * canvas.height,
|
||||
z: Math.random() * canvas.width,
|
||||
speed: Math.random() * 5 + 2, // Speed for falling effect
|
||||
});
|
||||
}
|
||||
|
||||
animate(); // Start animation
|
||||
});
|
||||
|
||||
// Function to animate the stars
|
||||
function animate() {
|
||||
const canvas = starsCanvas.value;
|
||||
if (!canvas) return;
|
||||
function hexToRgb() {
|
||||
let hex = props.color.replace(/^#/, "");
|
||||
|
||||
ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear canvas for each frame
|
||||
|
||||
for (const star of stars) {
|
||||
drawStar(star);
|
||||
|
||||
// Move star towards the screen (decrease z)
|
||||
star.z -= star.speed;
|
||||
|
||||
// Reset star when it reaches the viewer (z = 0)
|
||||
if (star.z <= 0) {
|
||||
star.z = canvas.width;
|
||||
star.x = (Math.random() - 0.5) * 2 * canvas.width;
|
||||
star.y = (Math.random() - 0.5) * 2 * canvas.height;
|
||||
}
|
||||
// If the hex code is 3 characters, expand it to 6 characters
|
||||
if (hex.length === 3) {
|
||||
hex = hex
|
||||
.split("")
|
||||
.map((char) => char + char)
|
||||
.join("");
|
||||
}
|
||||
|
||||
requestAnimationFrame(animate); // Continue animation
|
||||
// Parse the r, g, b values from the hex string
|
||||
const bigint = parseInt(hex, 16);
|
||||
const r = (bigint >> 16) & 255; // Extract the red component
|
||||
const g = (bigint >> 8) & 255; // Extract the green component
|
||||
const b = bigint & 255; // Extract the blue component
|
||||
|
||||
// Return the RGB values as a string separated by spaces
|
||||
return {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
};
|
||||
}
|
||||
|
||||
// Function to draw a star with a sharp line and blurred trail
|
||||
@ -81,7 +86,7 @@ function drawStar(star: Star) {
|
||||
const canvas = starsCanvas.value;
|
||||
if (!canvas) return;
|
||||
|
||||
ctx = canvas.getContext('2d');
|
||||
ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
const scale = perspective / (perspective + star.z); // 3D perspective scale
|
||||
@ -123,26 +128,31 @@ function drawStar(star: Star) {
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
function hexToRgb() {
|
||||
let hex = props.color.replace(/^#/, '');
|
||||
// Function to animate the stars
|
||||
function animate() {
|
||||
const canvas = starsCanvas.value;
|
||||
if (!canvas) return;
|
||||
|
||||
// If the hex code is 3 characters, expand it to 6 characters
|
||||
if (hex.length === 3) {
|
||||
hex = [...hex].map((char) => char + char).join('');
|
||||
}
|
||||
ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
// Parse the r, g, b values from the hex string
|
||||
const bigint = Number.parseInt(hex, 16);
|
||||
const r = (bigint >> 16) & 255; // Extract the red component
|
||||
const g = (bigint >> 8) & 255; // Extract the green component
|
||||
const b = bigint & 255; // Extract the blue component
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear canvas for each frame
|
||||
|
||||
// Return the RGB values as a string separated by spaces
|
||||
return {
|
||||
b,
|
||||
g,
|
||||
r,
|
||||
};
|
||||
stars.forEach((star) => {
|
||||
drawStar(star);
|
||||
|
||||
// Move star towards the screen (decrease z)
|
||||
star.z -= star.speed;
|
||||
|
||||
// Reset star when it reaches the viewer (z = 0)
|
||||
if (star.z <= 0) {
|
||||
star.z = canvas.width;
|
||||
star.x = (Math.random() - 0.5) * 2 * canvas.width;
|
||||
star.y = (Math.random() - 0.5) * 2 * canvas.height;
|
||||
}
|
||||
});
|
||||
|
||||
requestAnimationFrame(animate); // Continue animation
|
||||
}
|
||||
|
||||
// Set canvas to full screen
|
||||
@ -154,7 +164,3 @@ function resizeCanvas() {
|
||||
canvas.height = canvas.clientHeight;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<canvas ref="starsCanvas" :class="cn('absolute inset-0 w-full h-full', $props.class)"></canvas>
|
||||
</template>
|
1
src/components/InspiraUI/bg-falling-stars/index.ts
Normal file
1
src/components/InspiraUI/bg-falling-stars/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as FallingStarsBg } from "./FallingStarsBg.vue";
|
65
src/components/InspiraUI/border-beam/BorderBeam.vue
Normal file
65
src/components/InspiraUI/border-beam/BorderBeam.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'border-beam',
|
||||
'pointer-events-none absolute inset-0 rounded-[inherit] [border:calc(var(--border-width)*1px)_solid_transparent]',
|
||||
'![mask-clip:padding-box,border-box] ![mask-composite:intersect] [mask:linear-gradient(transparent,transparent),linear-gradient(white,white)]',
|
||||
'after:absolute after:aspect-square after:w-[calc(var(--size)*1px)] animate-border-beam after:[animation-delay:var(--delay)] after:[background:linear-gradient(to_left,var(--color-from),var(--color-to),transparent)] after:[offset-anchor:calc(var(--anchor)*1%)_50%] after:[offset-path:rect(0_auto_auto_0_round_calc(var(--size)*1px))]',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { cn } from "@/shadcn/lib/utils";
|
||||
import { computed } from "vue";
|
||||
|
||||
interface BorderBeamProps {
|
||||
class?: string;
|
||||
size?: number;
|
||||
duration?: number;
|
||||
borderWidth?: number;
|
||||
anchor?: number;
|
||||
colorFrom?: string;
|
||||
colorTo?: string;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<BorderBeamProps>(), {
|
||||
size: 200,
|
||||
duration: 15000,
|
||||
anchor: 90,
|
||||
borderWidth: 1.5,
|
||||
colorFrom: "#ffaa40",
|
||||
colorTo: "#9c40ff",
|
||||
delay: 0,
|
||||
});
|
||||
|
||||
const durationInSeconds = computed(() => `${props.duration}s`);
|
||||
const delayInSeconds = computed(() => `${props.delay}s`);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.border-beam {
|
||||
--size: v-bind(size);
|
||||
--duration: v-bind(durationInSeconds);
|
||||
--anchor: v-bind(anchor);
|
||||
--border-width: v-bind(borderWidth);
|
||||
--color-from: v-bind(colorFrom);
|
||||
--color-to: v-bind(colorTo);
|
||||
--delay: v-bind(delayInSeconds);
|
||||
}
|
||||
|
||||
.animate-border-beam::after {
|
||||
content: "";
|
||||
animation: border-beam-anim var(--duration) infinite linear;
|
||||
}
|
||||
|
||||
@keyframes border-beam-anim {
|
||||
to {
|
||||
offset-distance: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
1
src/components/InspiraUI/border-beam/index.ts
Normal file
1
src/components/InspiraUI/border-beam/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as BorderBeam } from "./BorderBeam.vue";
|
@ -1,54 +1,3 @@
|
||||
<!-- https://inspira-ui.com/components/cards/card-spotlight -->
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/shadcn/lib/utils';
|
||||
import { computed, type HTMLAttributes, onMounted, ref } from 'vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
class?: HTMLAttributes['class'];
|
||||
gradientColor?: string;
|
||||
gradientOpacity?: number;
|
||||
gradientSize?: number;
|
||||
slotClass?: HTMLAttributes['class'];
|
||||
}>(),
|
||||
{
|
||||
class: '',
|
||||
gradientColor: '#262626',
|
||||
gradientOpacity: 0.8,
|
||||
gradientSize: 200,
|
||||
slotClass: '',
|
||||
},
|
||||
);
|
||||
|
||||
const mouseX = ref(-props.gradientSize * 10);
|
||||
const mouseY = ref(-props.gradientSize * 10);
|
||||
|
||||
function handleMouseLeave() {
|
||||
mouseX.value = -props.gradientSize * 10;
|
||||
mouseY.value = -props.gradientSize * 10;
|
||||
}
|
||||
|
||||
function handleMouseMove(e: MouseEvent) {
|
||||
const target = e.currentTarget as HTMLElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
mouseX.value = e.clientX - rect.left;
|
||||
mouseY.value = e.clientY - rect.top;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
mouseX.value = -props.gradientSize * 10;
|
||||
mouseY.value = -props.gradientSize * 10;
|
||||
});
|
||||
|
||||
const backgroundStyle = computed(() => {
|
||||
return `radial-gradient(
|
||||
circle at ${mouseX.value}px ${mouseY.value}px,
|
||||
${props.gradientColor} 0%,
|
||||
rgba(0, 0, 0, 0) 70%
|
||||
)`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
@ -70,3 +19,53 @@ const backgroundStyle = computed(() => {
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { cn } from "@/shadcn/lib/utils";
|
||||
import { ref, computed, onMounted, type HTMLAttributes } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
slotClass?: HTMLAttributes["class"];
|
||||
gradientSize?: number;
|
||||
gradientColor?: string;
|
||||
gradientOpacity?: number;
|
||||
}>(),
|
||||
{
|
||||
class: "",
|
||||
slotClass: "",
|
||||
gradientSize: 200,
|
||||
gradientColor: "#262626",
|
||||
gradientOpacity: 0.8,
|
||||
},
|
||||
);
|
||||
|
||||
const mouseX = ref(-props.gradientSize * 10);
|
||||
const mouseY = ref(-props.gradientSize * 10);
|
||||
|
||||
function handleMouseMove(e: MouseEvent) {
|
||||
const target = e.currentTarget as HTMLElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
mouseX.value = e.clientX - rect.left;
|
||||
mouseY.value = e.clientY - rect.top;
|
||||
}
|
||||
|
||||
function handleMouseLeave() {
|
||||
mouseX.value = -props.gradientSize * 10;
|
||||
mouseY.value = -props.gradientSize * 10;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
mouseX.value = -props.gradientSize * 10;
|
||||
mouseY.value = -props.gradientSize * 10;
|
||||
});
|
||||
|
||||
const backgroundStyle = computed(() => {
|
||||
return `radial-gradient(
|
||||
circle at ${mouseX.value}px ${mouseY.value}px,
|
||||
${props.gradientColor} 0%,
|
||||
rgba(0, 0, 0, 0) 70%
|
||||
)`;
|
||||
});
|
||||
</script>
|
1
src/components/InspiraUI/card-spotlight/index.ts
Normal file
1
src/components/InspiraUI/card-spotlight/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as CardSpotlight } from "./CardSpotlight.vue";
|
76
src/components/InspiraUI/dock/Dock.vue
Normal file
76
src/components/InspiraUI/dock/Dock.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div
|
||||
ref="dockRef"
|
||||
:class="
|
||||
cn(
|
||||
'supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max rounded-2xl border p-2 backdrop-blur-md transition-all gap-4',
|
||||
orientation === 'vertical' && 'flex-col w-[58px] h-max',
|
||||
props.class,
|
||||
dockClass,
|
||||
)
|
||||
"
|
||||
@mousemove="onMouseMove"
|
||||
@mouseleave="onMouseLeave"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { DataOrientation, Direction } from "./types";
|
||||
import {
|
||||
MOUSE_X_INJECTION_KEY,
|
||||
MOUSE_Y_INJECTION_KEY,
|
||||
MAGNIFICATION_INJECTION_KEY,
|
||||
DISTANCE_INJECTION_KEY,
|
||||
ORIENTATION_INJECTION_KEY,
|
||||
} from "./injectionKeys";
|
||||
import { cn } from "@/shadcn/lib/utils";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
|
||||
interface DockProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
magnification?: number;
|
||||
distance?: number;
|
||||
direction?: Direction;
|
||||
orientation?: DataOrientation;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<DockProps>(), {
|
||||
magnification: 60,
|
||||
distance: 140,
|
||||
direction: "middle",
|
||||
orientation: "horizontal",
|
||||
});
|
||||
|
||||
const dockRef = ref<HTMLElement | null>(null);
|
||||
const mouseX = ref(Infinity);
|
||||
const mouseY = ref(Infinity);
|
||||
const magnification = computed(() => props.magnification);
|
||||
const distance = computed(() => props.distance);
|
||||
|
||||
const dockClass = computed(() => ({
|
||||
"items-start": props.direction === "top",
|
||||
"items-center": props.direction === "middle",
|
||||
"items-end": props.direction === "bottom",
|
||||
}));
|
||||
|
||||
function onMouseMove(e: MouseEvent) {
|
||||
requestAnimationFrame(() => {
|
||||
mouseX.value = e.pageX;
|
||||
mouseY.value = e.pageY;
|
||||
});
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
requestAnimationFrame(() => {
|
||||
mouseX.value = Infinity;
|
||||
mouseY.value = Infinity;
|
||||
});
|
||||
}
|
||||
provide(MOUSE_X_INJECTION_KEY, mouseX);
|
||||
provide(MOUSE_Y_INJECTION_KEY, mouseY);
|
||||
provide(ORIENTATION_INJECTION_KEY, props.orientation);
|
||||
provide(MAGNIFICATION_INJECTION_KEY, magnification);
|
||||
provide(DISTANCE_INJECTION_KEY, distance);
|
||||
</script>
|
61
src/components/InspiraUI/dock/DockIcon.vue
Normal file
61
src/components/InspiraUI/dock/DockIcon.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div
|
||||
ref="iconRef"
|
||||
class="flex aspect-square cursor-pointer items-center justify-center rounded-full transition-all duration-200 ease-out"
|
||||
:style="{
|
||||
width: `${iconWidth}px`,
|
||||
height: `${iconWidth}px`,
|
||||
}"
|
||||
:hovered="{
|
||||
marginLeft: margin,
|
||||
marginRight: margin,
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
MOUSE_X_INJECTION_KEY,
|
||||
MOUSE_Y_INJECTION_KEY,
|
||||
MAGNIFICATION_INJECTION_KEY,
|
||||
DISTANCE_INJECTION_KEY,
|
||||
ORIENTATION_INJECTION_KEY,
|
||||
} from "./injectionKeys";
|
||||
|
||||
const iconRef = ref<HTMLDivElement | null>(null);
|
||||
|
||||
const mouseX = inject(MOUSE_X_INJECTION_KEY, ref(Infinity));
|
||||
const mouseY = inject(MOUSE_Y_INJECTION_KEY, ref(Infinity));
|
||||
const distance = inject(DISTANCE_INJECTION_KEY);
|
||||
const orientation = inject(ORIENTATION_INJECTION_KEY, "vertical");
|
||||
const magnification = inject(MAGNIFICATION_INJECTION_KEY);
|
||||
const isVertical = computed(() => orientation === "vertical");
|
||||
|
||||
const margin = ref(0);
|
||||
|
||||
function calculateDistance(val: number) {
|
||||
if (isVertical.value) {
|
||||
const bounds = iconRef.value?.getBoundingClientRect() || {
|
||||
y: 0,
|
||||
height: 0,
|
||||
};
|
||||
return val - bounds.y - bounds.height / 2;
|
||||
}
|
||||
const bounds = iconRef.value?.getBoundingClientRect() || { x: 0, width: 0 };
|
||||
return val - bounds.x - bounds.width / 2;
|
||||
}
|
||||
|
||||
const iconWidth = computed(() => {
|
||||
const distanceCalc = isVertical.value
|
||||
? calculateDistance(mouseY.value)
|
||||
: calculateDistance(mouseX.value);
|
||||
if (!distance?.value || !magnification?.value) return 40;
|
||||
if (Math.abs(distanceCalc) < distance?.value) {
|
||||
return (1 - Math.abs(distanceCalc) / distance?.value) * magnification?.value + 40;
|
||||
}
|
||||
|
||||
return 40;
|
||||
});
|
||||
</script>
|
14
src/components/InspiraUI/dock/DockSeparator.vue
Normal file
14
src/components/InspiraUI/dock/DockSeparator.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn('relative block bg-secondary', orientation === 'vertical' ? 'w-4/5 h-0.5' : 'h-4/5 w-0.5')
|
||||
"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { cn } from "@/shadcn/lib/utils";
|
||||
import { ORIENTATION_INJECTION_KEY } from "./injectionKeys";
|
||||
|
||||
const orientation = inject(ORIENTATION_INJECTION_KEY, "vertical");
|
||||
</script>
|
5
src/components/InspiraUI/dock/index.ts
Normal file
5
src/components/InspiraUI/dock/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export { default as Dock } from "./Dock.vue";
|
||||
export { default as DockIcon } from "./DockIcon.vue";
|
||||
export { default as DockSeparator } from "./DockSeparator.vue";
|
||||
|
||||
export type DataOrientation = "vertical" | "horizontal";
|
11
src/components/InspiraUI/dock/injectionKeys.ts
Normal file
11
src/components/InspiraUI/dock/injectionKeys.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { Ref, InjectionKey, ComputedRef } from "vue";
|
||||
import type { DataOrientation } from "./types";
|
||||
|
||||
export const MOUSE_X_INJECTION_KEY = Symbol() as InjectionKey<Ref<number>>;
|
||||
export const MOUSE_Y_INJECTION_KEY = Symbol() as InjectionKey<Ref<number>>;
|
||||
|
||||
export const MAGNIFICATION_INJECTION_KEY = Symbol() as InjectionKey<ComputedRef<number>>;
|
||||
|
||||
export const DISTANCE_INJECTION_KEY = Symbol() as InjectionKey<ComputedRef<number>>;
|
||||
|
||||
export const ORIENTATION_INJECTION_KEY = Symbol() as InjectionKey<DataOrientation>;
|
2
src/components/InspiraUI/dock/types.ts
Normal file
2
src/components/InspiraUI/dock/types.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export type DataOrientation = "vertical" | "horizontal";
|
||||
export type Direction = "top" | "middle" | "bottom";
|
@ -1,34 +1,3 @@
|
||||
<!-- https://inspira-ui.com/components/buttons/gradient-button -->
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/shadcn/lib/utils';
|
||||
import { computed } from 'vue';
|
||||
|
||||
interface GradientButtonProps {
|
||||
bgColor?: string;
|
||||
blur?: number;
|
||||
borderRadius?: number;
|
||||
borderWidth?: number;
|
||||
class?: string;
|
||||
colors?: string[];
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<GradientButtonProps>(), {
|
||||
bgColor: '#000',
|
||||
blur: 4,
|
||||
borderRadius: 8,
|
||||
borderWidth: 2,
|
||||
colors: () => ['#FF0000', '#FFA500', '#FFFF00', '#008000', '#0000FF', '#4B0082', '#EE82EE', '#FF0000'],
|
||||
duration: 2500,
|
||||
});
|
||||
|
||||
const durationInMilliseconds = computed(() => `${props.duration}ms`);
|
||||
const allColors = computed(() => props.colors.join(', '));
|
||||
const borderWidthInPx = computed(() => `${props.borderWidth}px`);
|
||||
const borderRadiusInPx = computed(() => `${props.borderRadius}px`);
|
||||
const blurPx = computed(() => `${props.blur}px`);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
:class="
|
||||
@ -44,9 +13,48 @@ const blurPx = computed(() => `${props.blur}px`);
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@/shadcn/lib/utils";
|
||||
import { computed } from "vue";
|
||||
|
||||
interface GradientButtonProps {
|
||||
borderWidth?: number;
|
||||
colors?: string[];
|
||||
duration?: number;
|
||||
borderRadius?: number;
|
||||
blur?: number;
|
||||
class?: string;
|
||||
bgColor?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<GradientButtonProps>(), {
|
||||
colors: () => [
|
||||
"#FF0000",
|
||||
"#FFA500",
|
||||
"#FFFF00",
|
||||
"#008000",
|
||||
"#0000FF",
|
||||
"#4B0082",
|
||||
"#EE82EE",
|
||||
"#FF0000",
|
||||
],
|
||||
duration: 2500,
|
||||
borderWidth: 2,
|
||||
borderRadius: 8,
|
||||
blur: 4,
|
||||
bgColor: "#000",
|
||||
});
|
||||
|
||||
const durationInMilliseconds = computed(() => `${props.duration}ms`);
|
||||
const allColors = computed(() => props.colors.join(", "));
|
||||
const borderWidthInPx = computed(() => `${props.borderWidth}px`);
|
||||
const borderRadiusInPx = computed(() => `${props.borderRadius}px`);
|
||||
const blurPx = computed(() => `${props.blur}px`);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.animate-rainbow::before {
|
||||
content: '';
|
||||
content: "";
|
||||
background: conic-gradient(v-bind(allColors));
|
||||
animation: rotate-rainbow v-bind(durationInMilliseconds) linear infinite;
|
||||
filter: blur(v-bind(blurPx));
|
1
src/components/InspiraUI/gradient-button/index.ts
Normal file
1
src/components/InspiraUI/gradient-button/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as GradientButton } from "./GradientButton.vue";
|
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<button
|
||||
ref="buttonRef"
|
||||
:class="
|
||||
cn(
|
||||
'group relative w-auto cursor-pointer overflow-hidden rounded-full border bg-background p-2 px-6 text-center font-semibold',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="size-2 scale-100 rounded-lg bg-primary transition-all duration-300 group-hover:scale-[100.8]"
|
||||
></div>
|
||||
<span
|
||||
class="inline-block whitespace-nowrap transition-all duration-300 group-hover:translate-x-12 group-hover:opacity-0"
|
||||
>
|
||||
{{ text }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute top-0 z-10 flex size-full translate-x-12 items-center justify-center gap-2 text-primary-foreground opacity-0 transition-all duration-300 group-hover:-translate-x-5 group-hover:opacity-100"
|
||||
>
|
||||
<span class="whitespace-nowrap">{{ text }}</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-arrow-right"
|
||||
>
|
||||
<path d="M5 12h14" />
|
||||
<path d="m12 5 7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@/shadcn/lib/utils";
|
||||
import { ref } from "vue";
|
||||
|
||||
interface Props {
|
||||
text?: string;
|
||||
class?: string;
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
text: "Button",
|
||||
});
|
||||
|
||||
const buttonRef = ref<HTMLButtonElement>();
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -0,0 +1 @@
|
||||
export { default as InteractiveHoverButton } from "./InteractiveHoverButton.vue";
|
107
src/components/InspiraUI/shimmer-button/ShimmerButton.vue
Normal file
107
src/components/InspiraUI/shimmer-button/ShimmerButton.vue
Normal file
@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<button
|
||||
:class="
|
||||
cn(
|
||||
'group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden whitespace-nowrap border border-white/10 px-6 py-3 text-white [background:var(--bg)] [border-radius:var(--radius)] dark:text-black',
|
||||
'transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px',
|
||||
$props.class,
|
||||
)
|
||||
"
|
||||
:style="{
|
||||
'--spread': '90deg',
|
||||
'--shimmer-color': shimmerColor,
|
||||
'--radius': borderRadius,
|
||||
'--speed': shimmerDuration,
|
||||
'--cut': shimmerSize,
|
||||
'--bg': background,
|
||||
}"
|
||||
>
|
||||
<div :class="cn('-z-30 blur-[2px]', 'absolute inset-0 overflow-visible [container-type:size]')">
|
||||
<div
|
||||
class="animate-shimmer-btn-shimmer-slide absolute inset-0 h-[100cqh] [aspect-ratio:1] [border-radius:0] [mask:none]"
|
||||
>
|
||||
<div
|
||||
class="animate-shimmer-btn-spin-around absolute -inset-full w-auto rotate-0 [background:conic-gradient(from_calc(270deg-(var(--spread)*0.5)),transparent_0,var(--shimmer-color)_var(--spread),transparent_var(--spread))] [translate:0_0]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<slot />
|
||||
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'insert-0 absolute size-full',
|
||||
|
||||
'rounded-2xl px-4 py-1.5 text-sm font-medium shadow-[inset_0_-8px_10px_#ffffff1f]',
|
||||
|
||||
// transition
|
||||
'transform-gpu transition-all duration-300 ease-in-out',
|
||||
|
||||
// on hover
|
||||
'group-hover:shadow-[inset_0_-6px_10px_#ffffff3f]',
|
||||
|
||||
// on click
|
||||
'group-active:shadow-[inset_0_-10px_10px_#ffffff3f]',
|
||||
)
|
||||
"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="absolute -z-20 [background:var(--bg)] [border-radius:var(--radius)] [inset:var(--cut)]"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/shadcn/lib/utils';
|
||||
|
||||
type ShimmerButtonProps = {
|
||||
shimmerColor?: string;
|
||||
shimmerSize?: string;
|
||||
borderRadius?: string;
|
||||
shimmerDuration?: string;
|
||||
background?: string;
|
||||
class?: string;
|
||||
};
|
||||
|
||||
withDefaults(defineProps<ShimmerButtonProps>(), {
|
||||
shimmerColor: "#ffffff",
|
||||
shimmerSize: "0.05em",
|
||||
shimmerDuration: "3s",
|
||||
borderRadius: "100px",
|
||||
background: "rgba(0, 0, 0, 1)",
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@keyframes shimmer-btn-shimmer-slide {
|
||||
to {
|
||||
transform: translate(calc(100cqw - 100%), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shimmer-btn-spin-around {
|
||||
0% {
|
||||
transform: translateZ(0) rotate(0);
|
||||
}
|
||||
15%,
|
||||
35% {
|
||||
transform: translateZ(0) rotate(90deg);
|
||||
}
|
||||
65%,
|
||||
85% {
|
||||
transform: translateZ(0) rotate(270deg);
|
||||
}
|
||||
100% {
|
||||
transform: translateZ(0) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-shimmer-btn-shimmer-slide {
|
||||
animation: shimmer-btn-shimmer-slide var(--speed) ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.animate-shimmer-btn-spin-around {
|
||||
animation: shimmer-btn-spin-around calc(var(--speed) * 2) infinite linear;
|
||||
}
|
||||
</style>
|
1
src/components/InspiraUI/shimmer-button/index.ts
Normal file
1
src/components/InspiraUI/shimmer-button/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as ShimmerButton } from "./ShimmerButton.vue";
|
159
src/components/InspiraUI/sparkles/Sparkles.vue
Normal file
159
src/components/InspiraUI/sparkles/Sparkles.vue
Normal file
@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="relative size-full overflow-hidden will-change-transform"
|
||||
:style="{ background }"
|
||||
>
|
||||
<canvas
|
||||
ref="canvasRef"
|
||||
class="absolute inset-0 size-full"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRafFn, templateRef } from "@vueuse/core";
|
||||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
||||
|
||||
interface Props {
|
||||
background?: string;
|
||||
particleColor?: string;
|
||||
minSize?: number;
|
||||
maxSize?: number;
|
||||
speed?: number;
|
||||
particleDensity?: number;
|
||||
}
|
||||
|
||||
interface Particle {
|
||||
x: number;
|
||||
y: number;
|
||||
size: number;
|
||||
opacity: number;
|
||||
vx: number;
|
||||
vy: number;
|
||||
phase: number;
|
||||
phaseSpeed: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
background: "#0d47a1",
|
||||
particleColor: "#ffffff",
|
||||
minSize: 1,
|
||||
maxSize: 3,
|
||||
speed: 4,
|
||||
particleDensity: 120,
|
||||
});
|
||||
|
||||
const containerRef = templateRef<HTMLElement | null>("containerRef");
|
||||
const canvasRef = templateRef<HTMLCanvasElement | null>("canvasRef");
|
||||
const particles = ref<Particle[]>([]);
|
||||
const ctx = ref<CanvasRenderingContext2D | null>(null);
|
||||
|
||||
// Adjust canvas size on mount and resize
|
||||
function resizeCanvas() {
|
||||
if (!canvasRef.value || !containerRef.value) return;
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const rect = containerRef.value.getBoundingClientRect();
|
||||
|
||||
canvasRef.value.width = rect.width * dpr;
|
||||
canvasRef.value.height = rect.height * dpr;
|
||||
|
||||
if (ctx.value) {
|
||||
ctx.value.scale(dpr, dpr);
|
||||
}
|
||||
}
|
||||
|
||||
function generateParticles(): void {
|
||||
const newParticles: Particle[] = [];
|
||||
const count = props.particleDensity;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const baseSpeed = 0.05;
|
||||
const speedVariance = Math.random() * 0.3 + 0.7;
|
||||
|
||||
newParticles.push({
|
||||
x: Math.random() * 100,
|
||||
y: Math.random() * 100,
|
||||
size: Math.random() * (props.maxSize - props.minSize) + props.minSize,
|
||||
opacity: Math.random() * 0.5 + 0.3,
|
||||
vx: (Math.random() - 0.5) * baseSpeed * speedVariance * props.speed,
|
||||
vy: ((Math.random() - 0.5) * baseSpeed - baseSpeed * 0.3) * speedVariance * props.speed,
|
||||
phase: Math.random() * Math.PI * 2,
|
||||
phaseSpeed: 0.015,
|
||||
});
|
||||
}
|
||||
|
||||
particles.value = newParticles;
|
||||
}
|
||||
|
||||
function updateAndDrawParticles() {
|
||||
if (!ctx.value || !canvasRef.value) return;
|
||||
|
||||
const canvas = canvasRef.value;
|
||||
ctx.value.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
particles.value = particles.value.map((particle) => {
|
||||
let newX = particle.x + particle.vx;
|
||||
let newY = particle.y + particle.vy;
|
||||
|
||||
if (newX < -2) newX = 102;
|
||||
if (newX > 102) newX = -2;
|
||||
if (newY < -2) newY = 102;
|
||||
if (newY > 102) newY = -2;
|
||||
|
||||
const newPhase = (particle.phase + particle.phaseSpeed) % (Math.PI * 2);
|
||||
const opacity = 0.3 + (Math.sin(newPhase) * 0.3 + 0.3);
|
||||
|
||||
// Draw particle
|
||||
ctx.value!.beginPath();
|
||||
ctx.value!.arc(
|
||||
(newX * canvas.width) / 100,
|
||||
(newY * canvas.height) / 100,
|
||||
particle.size,
|
||||
0,
|
||||
Math.PI * 2,
|
||||
);
|
||||
ctx.value!.fillStyle = `${props.particleColor}${Math.floor(opacity * 255)
|
||||
.toString(16)
|
||||
.padStart(2, "0")}`;
|
||||
ctx.value!.fill();
|
||||
|
||||
return {
|
||||
...particle,
|
||||
x: newX,
|
||||
y: newY,
|
||||
phase: newPhase,
|
||||
opacity,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const { pause, resume } = useRafFn(updateAndDrawParticles, { immediate: false });
|
||||
|
||||
// Handle window resize
|
||||
let resizeObserver: ResizeObserver | undefined;
|
||||
|
||||
onMounted(() => {
|
||||
if (!canvasRef.value) return;
|
||||
|
||||
ctx.value = canvasRef.value.getContext("2d");
|
||||
resizeCanvas();
|
||||
generateParticles();
|
||||
|
||||
// Set up resize observer
|
||||
resizeObserver = new ResizeObserver(resizeCanvas);
|
||||
if (containerRef.value) {
|
||||
resizeObserver.observe(containerRef.value);
|
||||
}
|
||||
|
||||
resume();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
pause();
|
||||
if (resizeObserver && containerRef.value) {
|
||||
resizeObserver.unobserve(containerRef.value);
|
||||
}
|
||||
});
|
||||
</script>
|
1
src/components/InspiraUI/sparkles/index.ts
Normal file
1
src/components/InspiraUI/sparkles/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Sparkles } from "./Sparkles.vue";
|
59
src/pages/Home.page.vue
Normal file
59
src/pages/Home.page.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<script setup lang="tsx">
|
||||
const VITE_BUILD_COMMIT = import.meta.env.VITE_BUILD_COMMIT;
|
||||
import { routes } from 'vue-router/auto-routes';
|
||||
|
||||
definePage({
|
||||
name: 'Home',
|
||||
meta: {
|
||||
title: '首页',
|
||||
hidden: true,
|
||||
},
|
||||
});
|
||||
import { useHead, useSeoMeta } from '@unhead/vue';
|
||||
|
||||
useHead({
|
||||
bodyAttrs: { class: { overflow: true } },
|
||||
title: 'Hello World',
|
||||
titleTemplate: (title) => `${title} | My App`,
|
||||
// Deduping
|
||||
// script: [{ key: '123', src: '/script.js' }],
|
||||
});
|
||||
|
||||
useSeoMeta({
|
||||
title: 'vue-ts-example 首页',
|
||||
description: 'vue-ts-example 首页',
|
||||
});
|
||||
|
||||
consola.info('routes', routes);
|
||||
|
||||
const FComponent: import('vue').FunctionalComponent<{ prop: string }> = (props /* context */) => (
|
||||
<>
|
||||
<a
|
||||
class="green"
|
||||
href="https://cn.vuejs.org/guide/extras/render-function#typing-functional-components"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
函数式组件: https://cn.vuejs.org/guide/extras/render-function#typing-functional-components
|
||||
</a>
|
||||
<p>函数式组件,接收到的 prop 值为:</p>
|
||||
<pre>{JSON.stringify(props, null, 2)}</pre>
|
||||
</>
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div b="1px solid pink" mt-2 p-2>
|
||||
<ul>
|
||||
<li>
|
||||
<router-link class="green" :to="{ name: 'DataLoadersId', params: { id: 520 } }">Data Loaders</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div b="1px solid pink" mt-2 p-2>
|
||||
<FComponent prop="Hello World" style="margin-top: 1rem"></FComponent>
|
||||
</div>
|
||||
|
||||
<SendSms class="mt-2!" />
|
||||
<div b="1px solid pink" mt-2 p-2>commit: {{ VITE_BUILD_COMMIT }}</div>
|
||||
</template>
|
@ -8,20 +8,20 @@ import SomeIcon from '~icons/svg/pacman';
|
||||
|
||||
<template>
|
||||
<div b="1px solid pink" mt-2 text-4 p-2 space-y-2>
|
||||
<!-- <div b="1px solid pink">
|
||||
<div b="1px solid pink">
|
||||
<div>@iconify-json/carbon/icons.json</div>
|
||||
<div i-carbon-face-cool text-orange />
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div b="1px solid pink">
|
||||
<div>Icons({ autoInstall: true })</div>
|
||||
<icon-carbon-face-cool class="text-yellow" w-8 h-8 />
|
||||
</div>
|
||||
|
||||
<!-- <div b="1px solid pink">
|
||||
<div b="1px solid pink">
|
||||
<div>pacman.svg</div>
|
||||
<div class="icon:pacman text-(pink)" />
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div b="1px solid pink">
|
||||
<div>pacman.svg</div>
|
||||
|
@ -19,4 +19,20 @@ const bgColor = computed(() => (isDarkTheme.value ? '#000' : '#fff'));
|
||||
Card Spotlight
|
||||
</CardSpotlight>
|
||||
</div>
|
||||
|
||||
<Dock class="mb-6">
|
||||
<DockIcon>
|
||||
<Icon-mdi-github class="size-full" />
|
||||
</DockIcon>
|
||||
<DockSeparator />
|
||||
<DockIcon>
|
||||
<Icon-mdi-google-drive class="size-full" />
|
||||
</DockIcon>
|
||||
<DockIcon>
|
||||
<Icon-logos-notion-icon class="size-full" />
|
||||
</DockIcon>
|
||||
<DockIcon>
|
||||
<Icon-logos-whatsapp-icon class="size-full" />
|
||||
</DockIcon>
|
||||
</Dock>
|
||||
</template>
|
||||
|
@ -1,33 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
PATTERN_BACKGROUND_DIRECTION,
|
||||
PATTERN_BACKGROUND_SPEED,
|
||||
PATTERN_BACKGROUND_VARIANT,
|
||||
} from '@/components/InspiraUI/pattern-background';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4 h-screen w-screen">
|
||||
<PatternBackground
|
||||
:animate="true"
|
||||
:direction="PATTERN_BACKGROUND_DIRECTION.TopRight"
|
||||
:variant="PATTERN_BACKGROUND_VARIANT.Dot"
|
||||
class="flex h-full w-full flex-col items-center gap-4 overflow-hidden rounded-lg border px-4 py-8"
|
||||
:speed="PATTERN_BACKGROUND_SPEED.Slow"
|
||||
>
|
||||
<h3
|
||||
class="relative z-20 bg-gradient-to-b from-neutral-200 to-neutral-500 bg-clip-text py-8 text-3xl font-bold text-transparent"
|
||||
>
|
||||
Spline
|
||||
</h3>
|
||||
<Spline scene="https://prod.spline.design/kZDDjO5HuC9GJUM2/scene.splinecode" />
|
||||
</PatternBackground>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: false
|
||||
</route>
|
@ -1,53 +1,85 @@
|
||||
<script setup lang="tsx">
|
||||
const VITE_BUILD_COMMIT = import.meta.env.VITE_BUILD_COMMIT;
|
||||
import { routes } from 'vue-router/auto-routes';
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
PATTERN_BACKGROUND_DIRECTION,
|
||||
PATTERN_BACKGROUND_SPEED,
|
||||
PATTERN_BACKGROUND_VARIANT,
|
||||
} from '@/components/InspiraUI/pattern-background';
|
||||
import { useLayout } from '@/layouts/sakai-vue/composables/layout';
|
||||
|
||||
definePage({ meta: { title: '首页' } });
|
||||
import { useHead, useSeoMeta } from '@unhead/vue';
|
||||
|
||||
useHead({
|
||||
bodyAttrs: { class: { overflow: true } },
|
||||
title: 'Hello World',
|
||||
titleTemplate: (title) => `${title} | My App`,
|
||||
// Deduping
|
||||
// script: [{ key: '123', src: '/script.js' }],
|
||||
});
|
||||
|
||||
useSeoMeta({
|
||||
description: '首页',
|
||||
title: 'Hello World',
|
||||
});
|
||||
|
||||
consola.info('routes', routes);
|
||||
|
||||
const FComponent: import('vue').FunctionalComponent<{ prop: string }> = (props /* context */) => (
|
||||
<>
|
||||
<a
|
||||
class="green"
|
||||
href="https://cn.vuejs.org/guide/extras/render-function#typing-functional-components"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
函数式组件: https://cn.vuejs.org/guide/extras/render-function#typing-functional-components
|
||||
</a>
|
||||
<p>函数式组件,接收到的 prop 值为:</p>
|
||||
<pre>{JSON.stringify(props, null, 2)}</pre>
|
||||
</>
|
||||
);
|
||||
const { isDarkTheme, toggleDarkMode } = useLayout();
|
||||
const GradientButton_bgColor = computed(() => (isDarkTheme.value ? '#000' : '#fff'));
|
||||
const particlesColor = computed(() => (isDarkTheme.value ? '#FFFFFF' : '#000000'));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div b="1px solid pink" mt-2 p-2>
|
||||
<ul>
|
||||
<li>
|
||||
<router-link class="green" :to="{ name: 'DataLoadersId', params: { id: 520 } }">Data Loaders</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div b="1px solid pink" mt-2 p-2>
|
||||
<FComponent prop="Hello World" style="margin-top: 1rem"></FComponent>
|
||||
</div>
|
||||
<div class="p-4 h-screen w-screen relative">
|
||||
<PatternBackground
|
||||
:animate="true"
|
||||
:direction="PATTERN_BACKGROUND_DIRECTION.TopRight"
|
||||
:variant="PATTERN_BACKGROUND_VARIANT.Dot"
|
||||
class="flex h-full w-full flex-col items-center gap-4 overflow-hidden border rounded-lg px-4 py-8"
|
||||
:speed="PATTERN_BACKGROUND_SPEED.Slow"
|
||||
shadow-2xl
|
||||
>
|
||||
<h3
|
||||
class="relative z-20 bg-gradient-to-b from-neutral-200 to-neutral-500 bg-clip-text py-0 text-3xl font-bold text-transparent"
|
||||
>
|
||||
Spline
|
||||
</h3>
|
||||
|
||||
<SendSms class="mt-2!" />
|
||||
<div b="1px solid pink" mt-2 p-2>commit: {{ VITE_BUILD_COMMIT }}</div>
|
||||
<RouterLink :to="{ name: 'Home' }">
|
||||
<GradientButton :bg-color="GradientButton_bgColor">Zooooooooooom 🚀 </GradientButton>
|
||||
<!-- <InteractiveHoverButton text="Zooooooooooom 🚀" /> -->
|
||||
</RouterLink>
|
||||
|
||||
<ShimmerButton class="isolate shadow-2xl" shimmer-size="2px" @click="toggleDarkMode">
|
||||
<span
|
||||
class="whitespace-pre-wrap text-center text-sm font-medium leading-none tracking-tight text-white lg:text-lg dark:from-white dark:to-slate-900/10"
|
||||
>
|
||||
{{ isDarkTheme ? '🌜' : '🌞' }}
|
||||
</span>
|
||||
</ShimmerButton>
|
||||
|
||||
<div class="relative h-40 w-[40rem]">
|
||||
<div
|
||||
class="absolute inset-x-20 top-0 h-[2px] w-3/4 bg-gradient-to-r from-transparent via-indigo-500 to-transparent blur-sm"
|
||||
/>
|
||||
<div
|
||||
class="absolute inset-x-20 top-0 h-px w-3/4 bg-gradient-to-r from-transparent via-indigo-500 to-transparent"
|
||||
/>
|
||||
<div
|
||||
class="absolute inset-x-60 top-0 h-[5px] w-1/4 bg-gradient-to-r from-transparent via-sky-500 to-transparent blur-sm"
|
||||
/>
|
||||
<div
|
||||
class="absolute inset-x-60 top-0 h-px w-1/4 bg-gradient-to-r from-transparent via-sky-500 to-transparent"
|
||||
/>
|
||||
|
||||
<Sparkles
|
||||
background="transparent"
|
||||
:min-size="0.4"
|
||||
:max-size="1.4"
|
||||
:particle-density="1200"
|
||||
class="size-full"
|
||||
:particle-color="particlesColor"
|
||||
/>
|
||||
|
||||
<div class="absolute inset-0 size-full [mask-image:radial-gradient(350px_200px_at_top,transparent_20%,white)]">
|
||||
<!-- bg-white dark:bg-black -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Spline scene="https://prod.spline.design/kZDDjO5HuC9GJUM2/scene.splinecode" />
|
||||
|
||||
<BorderBeam :size="250" :duration="12" :delay="9" :border-width="2" />
|
||||
</PatternBackground>
|
||||
|
||||
<FluidCursor />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: false
|
||||
</route>
|
||||
|
@ -22,9 +22,9 @@ router.onError((error) => {
|
||||
export { router, setupLayoutsResult };
|
||||
export function install({ app }: { app: import('vue').App<Element> }) {
|
||||
app
|
||||
// Register the plugin before the router
|
||||
// 在路由之前注册插件
|
||||
.use(DataLoaderPlugin, { router })
|
||||
// adding the router will trigger the initial navigation
|
||||
// 添加路由会触发初始导航
|
||||
.use(router);
|
||||
}
|
||||
// ========================================================================
|
||||
|
@ -1,20 +0,0 @@
|
||||
import 'vue-router';
|
||||
|
||||
// declare module 'vue-router' {
|
||||
// interface RouteMeta {
|
||||
// // fromList?: RouteLocationNormalized[]
|
||||
// }
|
||||
|
||||
// interface Router {
|
||||
// stack: {
|
||||
// // list: RouteLocationNormalizedLoaded[]
|
||||
// // currentStackIndex: number
|
||||
// }
|
||||
// }
|
||||
// interface RouteLocationNormalizedGeneric {
|
||||
// // from: RouteLocationNormalizedLoaded
|
||||
// // stackIndex: number
|
||||
// }
|
||||
// }
|
||||
|
||||
export {};
|
2
typed-router.d.ts
vendored
2
typed-router.d.ts
vendored
@ -24,7 +24,7 @@ declare module 'vue-router/auto-routes' {
|
||||
'DataLoadersId': RouteRecordInfo<'DataLoadersId', '/data-loaders/:id', { id: ParamValue<true> }, { id: ParamValue<false> }>,
|
||||
'DataLoadersIdSub1UserId': RouteRecordInfo<'DataLoadersIdSub1UserId', '/data-loaders/:id/sub-1/:userId', { id: ParamValue<true>, userId: ParamValue<true> }, { id: ParamValue<false>, userId: ParamValue<false> }>,
|
||||
'FlowbiteSidebar': RouteRecordInfo<'FlowbiteSidebar', '/FlowbiteSidebar', Record<never, never>, Record<never, never>>,
|
||||
'IndexNew': RouteRecordInfo<'IndexNew', '/index-new', Record<never, never>, Record<never, never>>,
|
||||
'Home': RouteRecordInfo<'Home', '/Home', Record<never, never>, Record<never, never>>,
|
||||
'PageAPI': RouteRecordInfo<'PageAPI', '/Page/API', Record<never, never>, Record<never, never>>,
|
||||
'PageFonts': RouteRecordInfo<'PageFonts', '/Page/fonts', Record<never, never>, Record<never, never>>,
|
||||
'PageIcons': RouteRecordInfo<'PageIcons', '/Page/Icons', Record<never, never>, Record<never, never>>,
|
||||
|
@ -37,6 +37,9 @@ export default defineConfig(({ command, mode }) => {
|
||||
// // 'vendor/nprogress': ['nprogress'],
|
||||
// // 'vendor/formkit': ['@formkit/auto-animate'],
|
||||
// },
|
||||
manualChunks: {
|
||||
'vendor/Cesium': ['cesium'],
|
||||
},
|
||||
},
|
||||
},
|
||||
sourcemap: mode !== 'production' || env.VITE_SOURCE_MAP === 'true',
|
||||
|
Reference in New Issue
Block a user