fancy-ui-svelte 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ interface ColorRGB {
2
+ r: number;
3
+ g: number;
4
+ b: number;
5
+ }
6
+ interface Props {
7
+ simResolution?: number;
8
+ dyeResolution?: number;
9
+ captureResolution?: number;
10
+ densityDissipation?: number;
11
+ velocityDissipation?: number;
12
+ pressure?: number;
13
+ pressureIterations?: number;
14
+ curl?: number;
15
+ splatRadius?: number;
16
+ splatForce?: number;
17
+ shading?: boolean;
18
+ colorUpdateSpeed?: number;
19
+ backColor?: ColorRGB | string;
20
+ transparent?: boolean;
21
+ fluidColor?: string;
22
+ fluidColors?: string[];
23
+ colorIntensity?: number;
24
+ class?: string;
25
+ autoSplat?: boolean;
26
+ autoSplatInterval?: number;
27
+ interactive?: boolean;
28
+ pauseWhenHidden?: boolean;
29
+ splatOnMount?: boolean;
30
+ }
31
+ declare const FluidCursorAdvanced: import("svelte").Component<Props, {}, "">;
32
+ type FluidCursorAdvanced = ReturnType<typeof FluidCursorAdvanced>;
33
+ export default FluidCursorAdvanced;
@@ -0,0 +1,86 @@
1
+ import { render, cleanup } from "@testing-library/svelte";
2
+ import { afterEach, beforeEach, describe, it, expect, vi } from "vitest";
3
+ import FluidCursorAdvanced from "./FluidCursorAdvanced.svelte";
4
+ describe("FluidCursorAdvanced", () => {
5
+ afterEach(cleanup);
6
+ it("renders a container div", () => {
7
+ const { container } = render(FluidCursorAdvanced);
8
+ const div = container.firstElementChild;
9
+ expect(div).toBeInTheDocument();
10
+ });
11
+ it("renders a canvas element", () => {
12
+ const { container } = render(FluidCursorAdvanced);
13
+ const canvas = container.querySelector("canvas");
14
+ expect(canvas).toBeInTheDocument();
15
+ });
16
+ it("canvas does not share the hard-coded id with FluidCursor", () => {
17
+ const { container } = render(FluidCursorAdvanced);
18
+ const canvas = container.querySelector("canvas");
19
+ expect(canvas?.id).not.toBe("fluid");
20
+ });
21
+ it("container has pointer-events-none class (contained mode)", () => {
22
+ const { container } = render(FluidCursorAdvanced);
23
+ const div = container.firstElementChild;
24
+ expect(div?.className).toContain("pointer-events-none");
25
+ });
26
+ it("container has absolute positioning in contained mode", () => {
27
+ const { container } = render(FluidCursorAdvanced);
28
+ const div = container.firstElementChild;
29
+ expect(div?.className).toContain("absolute");
30
+ expect(div?.className).toContain("inset-0");
31
+ });
32
+ it("canvas fills container in contained mode", () => {
33
+ const { container } = render(FluidCursorAdvanced);
34
+ const canvas = container.querySelector("canvas");
35
+ expect(canvas?.className).toContain("h-full");
36
+ expect(canvas?.className).toContain("w-full");
37
+ });
38
+ it("applies custom class names", () => {
39
+ const { container } = render(FluidCursorAdvanced, { props: { class: "my-advanced" } });
40
+ const div = container.firstElementChild;
41
+ expect(div?.className).toContain("my-advanced");
42
+ });
43
+ describe("interaction props", () => {
44
+ it("interactive={false} does not register mouse event listeners", () => {
45
+ const addSpy = vi.spyOn(window, "addEventListener");
46
+ render(FluidCursorAdvanced, { props: { interactive: false } });
47
+ const mousedownCalls = addSpy.mock.calls.filter(([event]) => event === "mousedown");
48
+ expect(mousedownCalls).toHaveLength(0);
49
+ const mousemoveCalls = addSpy.mock.calls.filter(([event]) => event === "mousemove");
50
+ expect(mousemoveCalls).toHaveLength(0);
51
+ addSpy.mockRestore();
52
+ });
53
+ it("mounts without error with autoSplat={true} and splatOnMount={true}", () => {
54
+ const { container } = render(FluidCursorAdvanced, {
55
+ props: { autoSplat: true, splatOnMount: true },
56
+ });
57
+ expect(container.querySelector("canvas")).toBeInTheDocument();
58
+ });
59
+ it("pauseWhenHidden={false} does not instantiate IntersectionObserver", () => {
60
+ const observeSpy = vi.fn();
61
+ const mockObserver = vi.fn(() => ({ observe: observeSpy, disconnect: vi.fn() }));
62
+ vi.stubGlobal("IntersectionObserver", mockObserver);
63
+ render(FluidCursorAdvanced, { props: { pauseWhenHidden: false } });
64
+ expect(mockObserver).not.toHaveBeenCalled();
65
+ vi.unstubAllGlobals();
66
+ });
67
+ });
68
+ describe("color props", () => {
69
+ it("renders without error when fluidColor is provided", () => {
70
+ const { container } = render(FluidCursorAdvanced, { props: { fluidColor: "#6366f1" } });
71
+ expect(container.querySelector("canvas")).toBeInTheDocument();
72
+ });
73
+ it("renders without error when fluidColors palette is provided", () => {
74
+ const { container } = render(FluidCursorAdvanced, {
75
+ props: { fluidColors: ["#6366f1", "#ec4899"] },
76
+ });
77
+ expect(container.querySelector("canvas")).toBeInTheDocument();
78
+ });
79
+ it("warns and falls back when an invalid hex color is passed", () => {
80
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
81
+ render(FluidCursorAdvanced, { props: { backColor: "not-a-color" } });
82
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining("[FluidCursorAdvanced] Invalid hex color"));
83
+ warn.mockRestore();
84
+ });
85
+ });
86
+ });
@@ -9,6 +9,7 @@ A WebGL-based fluid simulation that follows the cursor, creating beautiful flowi
9
9
  - Configurable simulation parameters
10
10
  - Transparent background support
11
11
  - Automatic canvas resizing
12
+ - Controllable fluid colors via hex strings (fixed color, palette cycling, or random)
12
13
 
13
14
  ## Implementation Notes
14
15
 
@@ -27,6 +28,30 @@ The component implements a full Navier-Stokes fluid simulation with:
27
28
  - Advection for both velocity and dye
28
29
  - Gradient subtraction for incompressible flow
29
30
 
31
+ ## Color Props
32
+
33
+ | Prop | Type | Default | Description |
34
+ |---|---|---|---|
35
+ | `fluidColor` | `string` (hex) | — | Fixed fluid color. Disables random cycling. |
36
+ | `fluidColors` | `string[]` (hex) | — | Palette to cycle through on each splat. |
37
+ | `colorIntensity` | `number` | `0.15` | Intensity multiplier applied to fluid colors (0–1). |
38
+ | `backColor` | `{ r, g, b }` or `string` (hex) | `{ r: 0.5, g: 0, b: 0 }` | Background color — accepts RGB object or hex string. |
39
+
40
+ **Priority**: `fluidColor` > `fluidColors` > random HSV
41
+
42
+ ### Examples
43
+
44
+ ```svelte
45
+ <!-- Fixed teal fluid -->
46
+ <FluidCursor fluidColor="#00ffcc" />
47
+
48
+ <!-- Cycling palette -->
49
+ <FluidCursor fluidColors={["#ff0080", "#00ffcc", "#7700ff"]} colorIntensity={0.3} />
50
+
51
+ <!-- Hex background -->
52
+ <FluidCursor backColor="#1a1a2e" />
53
+ ```
54
+
30
55
  ### Event Handling
31
56
 
32
57
  - Window-level mouse/touch events for full-screen tracking
@@ -1 +1,2 @@
1
1
  export { default as FluidCursor } from "./FluidCursor.svelte";
2
+ export { default as FluidCursorAdvanced } from "./FluidCursorAdvanced.svelte";
@@ -1 +1,2 @@
1
1
  export { default as FluidCursor } from "./FluidCursor.svelte";
2
+ export { default as FluidCursorAdvanced } from "./FluidCursorAdvanced.svelte";
@@ -13,6 +13,8 @@ export * from "./marquee/index.js";
13
13
  export * from "./compare/index.js";
14
14
  export * from "./image-trail-cursor/index.js";
15
15
  export * from "./interactive-grid-pattern/index.js";
16
+ export * from "./interactive-hover-button/index.js";
17
+ export * from "./gradient-button/index.js";
16
18
  export * from "./logo-cloud/index.js";
17
19
  export * from "./direction-aware-hover/index.js";
18
20
  export * from "./rainbow-button/index.js";
@@ -18,6 +18,8 @@ export * from "./marquee/index.js";
18
18
  export * from "./compare/index.js";
19
19
  export * from "./image-trail-cursor/index.js";
20
20
  export * from "./interactive-grid-pattern/index.js";
21
+ export * from "./interactive-hover-button/index.js";
22
+ export * from "./gradient-button/index.js";
21
23
  export * from "./logo-cloud/index.js";
22
24
  export * from "./direction-aware-hover/index.js";
23
25
  export * from "./rainbow-button/index.js";
@@ -117,10 +117,7 @@
117
117
 
118
118
  <span
119
119
  bind:this={spanRef}
120
- class={cn(
121
- "number-ticker inline-block tracking-wider text-black tabular-nums dark:text-white",
122
- className
123
- )}
120
+ class={cn("number-ticker inline-block text-black tabular-nums dark:text-white", className)}
124
121
  >
125
122
  {formattedValue}
126
123
  </span>
@@ -243,6 +243,16 @@ export const registry = {
243
243
  { source: "Inspira UI", url: "https://inspira-ui.com/components/cursor/fluid-cursor" },
244
244
  ],
245
245
  },
246
+ "fluid-cursor-advanced": {
247
+ name: "FluidCursorAdvanced",
248
+ slug: "fluid-cursor-advanced",
249
+ description: "WebGL fluid simulation confined to a parent container element",
250
+ category: "effects",
251
+ status: "done",
252
+ credits: [
253
+ { source: "Inspira UI", url: "https://inspira-ui.com/components/cursor/fluid-cursor" },
254
+ ],
255
+ },
246
256
  "glow-border": {
247
257
  name: "GlowBorder",
248
258
  slug: "glow-border",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fancy-ui-svelte",
3
3
  "private": false,
4
- "version": "0.4.0",
4
+ "version": "0.4.1",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -46,6 +46,7 @@
46
46
  "tailwindcss": "^4.0.0"
47
47
  },
48
48
  "dependencies": {
49
+ "@vercel/analytics": "^2.0.1",
49
50
  "canvas-confetti": "^1.9.4",
50
51
  "clsx": "^2.1.1",
51
52
  "gsap": "^3.14.2",