@farzadbagheri/fb-components 0.1.0

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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/ai.cjs +16 -0
  4. package/dist/ai.cjs.map +1 -0
  5. package/dist/ai.css +176 -0
  6. package/dist/ai.css.map +1 -0
  7. package/dist/ai.d.cts +33 -0
  8. package/dist/ai.d.ts +33 -0
  9. package/dist/ai.js +3 -0
  10. package/dist/ai.js.map +1 -0
  11. package/dist/backgrounds.cjs +32 -0
  12. package/dist/backgrounds.cjs.map +1 -0
  13. package/dist/backgrounds.css +253 -0
  14. package/dist/backgrounds.css.map +1 -0
  15. package/dist/backgrounds.d.cts +71 -0
  16. package/dist/backgrounds.d.ts +71 -0
  17. package/dist/backgrounds.js +3 -0
  18. package/dist/backgrounds.js.map +1 -0
  19. package/dist/buttons.cjs +20 -0
  20. package/dist/buttons.cjs.map +1 -0
  21. package/dist/buttons.css +116 -0
  22. package/dist/buttons.css.map +1 -0
  23. package/dist/buttons.d.cts +40 -0
  24. package/dist/buttons.d.ts +40 -0
  25. package/dist/buttons.js +3 -0
  26. package/dist/buttons.js.map +1 -0
  27. package/dist/chunk-4FWJRWS5.cjs +125 -0
  28. package/dist/chunk-4FWJRWS5.cjs.map +1 -0
  29. package/dist/chunk-BSEOOGOV.cjs +217 -0
  30. package/dist/chunk-BSEOOGOV.cjs.map +1 -0
  31. package/dist/chunk-C3ASXASL.cjs +205 -0
  32. package/dist/chunk-C3ASXASL.cjs.map +1 -0
  33. package/dist/chunk-CTD2GDO6.cjs +121 -0
  34. package/dist/chunk-CTD2GDO6.cjs.map +1 -0
  35. package/dist/chunk-EIE6FLCL.js +202 -0
  36. package/dist/chunk-EIE6FLCL.js.map +1 -0
  37. package/dist/chunk-H2AV67DY.cjs +260 -0
  38. package/dist/chunk-H2AV67DY.cjs.map +1 -0
  39. package/dist/chunk-H6Y3EJPA.cjs +83 -0
  40. package/dist/chunk-H6Y3EJPA.cjs.map +1 -0
  41. package/dist/chunk-JHOJY54P.js +123 -0
  42. package/dist/chunk-JHOJY54P.js.map +1 -0
  43. package/dist/chunk-JWV3IITG.js +80 -0
  44. package/dist/chunk-JWV3IITG.js.map +1 -0
  45. package/dist/chunk-K5QRX5CZ.js +194 -0
  46. package/dist/chunk-K5QRX5CZ.js.map +1 -0
  47. package/dist/chunk-MKCSRMHF.cjs +198 -0
  48. package/dist/chunk-MKCSRMHF.cjs.map +1 -0
  49. package/dist/chunk-R2F3FTBL.js +119 -0
  50. package/dist/chunk-R2F3FTBL.js.map +1 -0
  51. package/dist/chunk-ROKPXWY2.cjs +198 -0
  52. package/dist/chunk-ROKPXWY2.cjs.map +1 -0
  53. package/dist/chunk-RUSFF3MZ.js +214 -0
  54. package/dist/chunk-RUSFF3MZ.js.map +1 -0
  55. package/dist/chunk-T7CC3CLH.js +195 -0
  56. package/dist/chunk-T7CC3CLH.js.map +1 -0
  57. package/dist/chunk-UWQ5QNE3.cjs +311 -0
  58. package/dist/chunk-UWQ5QNE3.cjs.map +1 -0
  59. package/dist/chunk-WDUETPNG.js +256 -0
  60. package/dist/chunk-WDUETPNG.js.map +1 -0
  61. package/dist/chunk-YQSTEIU5.js +304 -0
  62. package/dist/chunk-YQSTEIU5.js.map +1 -0
  63. package/dist/data.cjs +20 -0
  64. package/dist/data.cjs.map +1 -0
  65. package/dist/data.css +151 -0
  66. package/dist/data.css.map +1 -0
  67. package/dist/data.d.cts +47 -0
  68. package/dist/data.d.ts +47 -0
  69. package/dist/data.js +3 -0
  70. package/dist/data.js.map +1 -0
  71. package/dist/heroes.cjs +12 -0
  72. package/dist/heroes.cjs.map +1 -0
  73. package/dist/heroes.css +190 -0
  74. package/dist/heroes.css.map +1 -0
  75. package/dist/heroes.d.cts +19 -0
  76. package/dist/heroes.d.ts +19 -0
  77. package/dist/heroes.js +3 -0
  78. package/dist/heroes.js.map +1 -0
  79. package/dist/index.cjs +104 -0
  80. package/dist/index.cjs.map +1 -0
  81. package/dist/index.css +1269 -0
  82. package/dist/index.css.map +1 -0
  83. package/dist/index.d.cts +10 -0
  84. package/dist/index.d.ts +10 -0
  85. package/dist/index.js +11 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/landing.cjs +12 -0
  88. package/dist/landing.cjs.map +1 -0
  89. package/dist/landing.css +137 -0
  90. package/dist/landing.css.map +1 -0
  91. package/dist/landing.d.cts +14 -0
  92. package/dist/landing.d.ts +14 -0
  93. package/dist/landing.js +3 -0
  94. package/dist/landing.js.map +1 -0
  95. package/dist/motion.cjs +16 -0
  96. package/dist/motion.cjs.map +1 -0
  97. package/dist/motion.css +79 -0
  98. package/dist/motion.css.map +1 -0
  99. package/dist/motion.d.cts +26 -0
  100. package/dist/motion.d.ts +26 -0
  101. package/dist/motion.js +3 -0
  102. package/dist/motion.js.map +1 -0
  103. package/dist/text.cjs +16 -0
  104. package/dist/text.cjs.map +1 -0
  105. package/dist/text.css +72 -0
  106. package/dist/text.css.map +1 -0
  107. package/dist/text.d.cts +28 -0
  108. package/dist/text.d.ts +28 -0
  109. package/dist/text.js +3 -0
  110. package/dist/text.js.map +1 -0
  111. package/dist/ui.cjs +16 -0
  112. package/dist/ui.cjs.map +1 -0
  113. package/dist/ui.css +95 -0
  114. package/dist/ui.css.map +1 -0
  115. package/dist/ui.d.cts +28 -0
  116. package/dist/ui.d.ts +28 -0
  117. package/dist/ui.js +3 -0
  118. package/dist/ui.js.map +1 -0
  119. package/package.json +134 -0
@@ -0,0 +1,214 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { Sparkles, Paperclip, ArrowUp } from 'lucide-react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ // src/ai/AiPromptBox.tsx
6
+ function AiPromptBox({
7
+ background = "#111116",
8
+ accent = "#9b7cff",
9
+ textColor = "#ffffff",
10
+ radius = 20,
11
+ minHeight = 150,
12
+ showModel = true,
13
+ showAttachment = true,
14
+ modelLabel = "FB AI \xB7 Fast",
15
+ placeholder = "Ask anything...",
16
+ defaultValue = "",
17
+ disabled = false,
18
+ onSubmit,
19
+ className = "",
20
+ style,
21
+ ...props
22
+ }) {
23
+ const [
24
+ value,
25
+ setValue
26
+ ] = useState(
27
+ defaultValue
28
+ );
29
+ function handleSubmit(event) {
30
+ event.preventDefault();
31
+ const trimmed = value.trim();
32
+ if (disabled || !trimmed) {
33
+ return;
34
+ }
35
+ onSubmit?.(
36
+ trimmed
37
+ );
38
+ }
39
+ return /* @__PURE__ */ jsx(
40
+ "div",
41
+ {
42
+ ...props,
43
+ className: `fb-ai-prompt-box ${className}`,
44
+ style: {
45
+ ...style,
46
+ background,
47
+ color: textColor,
48
+ borderRadius: radius,
49
+ minHeight,
50
+ ["--fb-ai-prompt-accent"]: accent
51
+ },
52
+ children: /* @__PURE__ */ jsxs(
53
+ "form",
54
+ {
55
+ className: "fb-ai-prompt-box__form",
56
+ onSubmit: handleSubmit,
57
+ children: [
58
+ /* @__PURE__ */ jsxs("div", { className: "fb-ai-prompt-box__field", children: [
59
+ /* @__PURE__ */ jsx(
60
+ Sparkles,
61
+ {
62
+ className: "fb-ai-prompt-box__spark",
63
+ size: 16,
64
+ "aria-hidden": "true"
65
+ }
66
+ ),
67
+ /* @__PURE__ */ jsx(
68
+ "textarea",
69
+ {
70
+ value,
71
+ disabled,
72
+ placeholder,
73
+ "aria-label": placeholder,
74
+ onChange: (event) => setValue(
75
+ event.target.value
76
+ )
77
+ }
78
+ )
79
+ ] }),
80
+ /* @__PURE__ */ jsxs("div", { className: "fb-ai-prompt-box__footer", children: [
81
+ /* @__PURE__ */ jsxs("div", { className: "fb-ai-prompt-box__actions", children: [
82
+ showAttachment && /* @__PURE__ */ jsx(
83
+ "button",
84
+ {
85
+ type: "button",
86
+ "aria-label": "Attach file",
87
+ className: "fb-ai-prompt-box__secondary",
88
+ children: /* @__PURE__ */ jsx(
89
+ Paperclip,
90
+ {
91
+ size: 15,
92
+ "aria-hidden": "true"
93
+ }
94
+ )
95
+ }
96
+ ),
97
+ showModel && /* @__PURE__ */ jsx("span", { className: "fb-ai-prompt-box__model", children: modelLabel })
98
+ ] }),
99
+ /* @__PURE__ */ jsx(
100
+ "button",
101
+ {
102
+ type: "submit",
103
+ disabled: disabled || !value.trim(),
104
+ className: "fb-ai-prompt-box__submit",
105
+ "aria-label": "Submit prompt",
106
+ children: /* @__PURE__ */ jsx(
107
+ ArrowUp,
108
+ {
109
+ size: 16,
110
+ "aria-hidden": "true"
111
+ }
112
+ )
113
+ }
114
+ )
115
+ ] })
116
+ ]
117
+ }
118
+ )
119
+ }
120
+ );
121
+ }
122
+ function StreamingResponse({
123
+ text = "FB Components gives your application a reusable system for motion, interaction and AI-native interfaces.",
124
+ accent = "#9b7cff",
125
+ textColor = "#ffffff",
126
+ speed = 24,
127
+ showCursor = true,
128
+ showStatus = true,
129
+ statusLabel = "GENERATING",
130
+ className = "",
131
+ style,
132
+ ...props
133
+ }) {
134
+ const [
135
+ length,
136
+ setLength
137
+ ] = useState(0);
138
+ useEffect(() => {
139
+ setLength(
140
+ 0
141
+ );
142
+ if (text.length === 0) {
143
+ return;
144
+ }
145
+ const interval = window.setInterval(
146
+ () => {
147
+ setLength(
148
+ (current) => {
149
+ if (current >= text.length) {
150
+ window.clearInterval(
151
+ interval
152
+ );
153
+ return current;
154
+ }
155
+ return current + 1;
156
+ }
157
+ );
158
+ },
159
+ Math.max(
160
+ 5,
161
+ speed
162
+ )
163
+ );
164
+ return () => window.clearInterval(
165
+ interval
166
+ );
167
+ }, [
168
+ text,
169
+ speed
170
+ ]);
171
+ const complete = length >= text.length;
172
+ return /* @__PURE__ */ jsxs(
173
+ "div",
174
+ {
175
+ ...props,
176
+ className: `fb-streaming-response ${className}`,
177
+ style: {
178
+ ...style,
179
+ color: textColor,
180
+ ["--fb-streaming-accent"]: accent
181
+ },
182
+ "aria-live": "polite",
183
+ children: [
184
+ showStatus && /* @__PURE__ */ jsxs("div", { className: "fb-streaming-response__status", children: [
185
+ /* @__PURE__ */ jsx(
186
+ "span",
187
+ {
188
+ className: complete ? "is-complete" : "",
189
+ "aria-hidden": "true"
190
+ }
191
+ ),
192
+ complete ? "COMPLETE" : statusLabel
193
+ ] }),
194
+ /* @__PURE__ */ jsxs("p", { children: [
195
+ text.slice(
196
+ 0,
197
+ length
198
+ ),
199
+ showCursor && !complete && /* @__PURE__ */ jsx(
200
+ "span",
201
+ {
202
+ className: "fb-streaming-response__cursor",
203
+ "aria-hidden": "true"
204
+ }
205
+ )
206
+ ] })
207
+ ]
208
+ }
209
+ );
210
+ }
211
+
212
+ export { AiPromptBox, StreamingResponse };
213
+ //# sourceMappingURL=chunk-RUSFF3MZ.js.map
214
+ //# sourceMappingURL=chunk-RUSFF3MZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ai/AiPromptBox.tsx","../src/ai/StreamingResponse.tsx"],"names":["useState","jsxs","jsx"],"mappings":"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","file":"chunk-RUSFF3MZ.js","sourcesContent":["import {\r\n useState,\r\n} from \"react\";\r\n\r\nimport type {\r\n FormEvent,\r\n HTMLAttributes,\r\n} from \"react\";\r\n\r\nimport {\r\n ArrowUp,\r\n Paperclip,\r\n Sparkles,\r\n} from \"lucide-react\";\r\n\r\nimport \"./AiPromptBox.css\";\r\n\r\nexport interface AiPromptBoxProps\r\n extends Omit<\r\n HTMLAttributes<HTMLDivElement>,\r\n \"onSubmit\"\r\n > {\r\n background?: string;\r\n\r\n accent?: string;\r\n\r\n textColor?: string;\r\n\r\n radius?: number;\r\n\r\n minHeight?: number;\r\n\r\n showModel?: boolean;\r\n\r\n showAttachment?: boolean;\r\n\r\n modelLabel?: string;\r\n\r\n placeholder?: string;\r\n\r\n defaultValue?: string;\r\n\r\n disabled?: boolean;\r\n\r\n onSubmit?: (\r\n value: string\r\n ) => void;\r\n\r\n className?: string;\r\n}\r\n\r\nexport function AiPromptBox({\r\n background = \"#111116\",\r\n\r\n accent = \"#9b7cff\",\r\n\r\n textColor = \"#ffffff\",\r\n\r\n radius = 20,\r\n\r\n minHeight = 150,\r\n\r\n showModel = true,\r\n\r\n showAttachment = true,\r\n\r\n modelLabel = \"FB AI · Fast\",\r\n\r\n placeholder =\r\n \"Ask anything...\",\r\n\r\n defaultValue = \"\",\r\n\r\n disabled = false,\r\n\r\n onSubmit,\r\n\r\n className = \"\",\r\n\r\n style,\r\n\r\n ...props\r\n}: AiPromptBoxProps) {\r\n const [\r\n value,\r\n setValue,\r\n ] =\r\n useState(\r\n defaultValue\r\n );\r\n\r\n function handleSubmit(\r\n event:\r\n FormEvent<HTMLFormElement>\r\n ) {\r\n event.preventDefault();\r\n\r\n const trimmed =\r\n value.trim();\r\n\r\n if (\r\n disabled ||\r\n !trimmed\r\n ) {\r\n return;\r\n }\r\n\r\n onSubmit?.(\r\n trimmed\r\n );\r\n }\r\n\r\n return (\r\n <div\r\n {...props}\r\n className={`fb-ai-prompt-box ${className}`}\r\n style={{\r\n ...style,\r\n\r\n background,\r\n\r\n color:\r\n textColor,\r\n\r\n borderRadius:\r\n radius,\r\n\r\n minHeight,\r\n\r\n [\"--fb-ai-prompt-accent\" as string]:\r\n accent,\r\n }}\r\n >\r\n <form\r\n className=\"fb-ai-prompt-box__form\"\r\n onSubmit={\r\n handleSubmit\r\n }\r\n >\r\n <div className=\"fb-ai-prompt-box__field\">\r\n <Sparkles\r\n className=\"fb-ai-prompt-box__spark\"\r\n size={16}\r\n aria-hidden=\"true\"\r\n />\r\n\r\n <textarea\r\n value={\r\n value\r\n }\r\n disabled={\r\n disabled\r\n }\r\n placeholder={\r\n placeholder\r\n }\r\n aria-label={\r\n placeholder\r\n }\r\n onChange={(\r\n event\r\n ) =>\r\n setValue(\r\n event.target\r\n .value\r\n )\r\n }\r\n />\r\n </div>\r\n\r\n <div className=\"fb-ai-prompt-box__footer\">\r\n <div className=\"fb-ai-prompt-box__actions\">\r\n {showAttachment && (\r\n <button\r\n type=\"button\"\r\n aria-label=\"Attach file\"\r\n className=\"fb-ai-prompt-box__secondary\"\r\n >\r\n <Paperclip\r\n size={15}\r\n aria-hidden=\"true\"\r\n />\r\n </button>\r\n )}\r\n\r\n {showModel && (\r\n <span className=\"fb-ai-prompt-box__model\">\r\n {\r\n modelLabel\r\n }\r\n </span>\r\n )}\r\n </div>\r\n\r\n <button\r\n type=\"submit\"\r\n disabled={\r\n disabled ||\r\n !value.trim()\r\n }\r\n className=\"fb-ai-prompt-box__submit\"\r\n aria-label=\"Submit prompt\"\r\n >\r\n <ArrowUp\r\n size={16}\r\n aria-hidden=\"true\"\r\n />\r\n </button>\r\n </div>\r\n </form>\r\n </div>\r\n );\r\n}","import {\r\n useEffect,\r\n useState,\r\n} from \"react\";\r\n\r\nimport type {\r\n HTMLAttributes,\r\n} from \"react\";\r\n\r\nimport \"./StreamingResponse.css\";\r\n\r\nexport interface StreamingResponseProps\r\n extends HTMLAttributes<HTMLDivElement> {\r\n text?: string;\r\n\r\n accent?: string;\r\n textColor?: string;\r\n\r\n speed?: number;\r\n\r\n showCursor?: boolean;\r\n showStatus?: boolean;\r\n\r\n statusLabel?: string;\r\n\r\n className?: string;\r\n}\r\n\r\nexport function StreamingResponse({\r\n text =\r\n \"FB Components gives your application a reusable system for motion, interaction and AI-native interfaces.\",\r\n\r\n accent = \"#9b7cff\",\r\n textColor = \"#ffffff\",\r\n\r\n speed = 24,\r\n\r\n showCursor = true,\r\n showStatus = true,\r\n\r\n statusLabel =\r\n \"GENERATING\",\r\n\r\n className = \"\",\r\n style,\r\n\r\n ...props\r\n}: StreamingResponseProps) {\r\n const [\r\n length,\r\n setLength,\r\n ] =\r\n useState(0);\r\n\r\n useEffect(() => {\r\n setLength(\r\n 0\r\n );\r\n\r\n if (\r\n text.length ===\r\n 0\r\n ) {\r\n return;\r\n }\r\n\r\n const interval =\r\n window.setInterval(\r\n () => {\r\n setLength(\r\n (\r\n current\r\n ) => {\r\n if (\r\n current >=\r\n text.length\r\n ) {\r\n window.clearInterval(\r\n interval\r\n );\r\n\r\n return current;\r\n }\r\n\r\n return (\r\n current +\r\n 1\r\n );\r\n }\r\n );\r\n },\r\n Math.max(\r\n 5,\r\n speed\r\n )\r\n );\r\n\r\n return () =>\r\n window.clearInterval(\r\n interval\r\n );\r\n }, [\r\n text,\r\n speed,\r\n ]);\r\n\r\n const complete =\r\n length >=\r\n text.length;\r\n\r\n return (\r\n <div\r\n {...props}\r\n className={`fb-streaming-response ${className}`}\r\n style={{\r\n ...style,\r\n\r\n color:\r\n textColor,\r\n\r\n [\"--fb-streaming-accent\" as string]:\r\n accent,\r\n }}\r\n aria-live=\"polite\"\r\n >\r\n {showStatus && (\r\n <div className=\"fb-streaming-response__status\">\r\n <span\r\n className={\r\n complete\r\n ? \"is-complete\"\r\n : \"\"\r\n }\r\n aria-hidden=\"true\"\r\n />\r\n\r\n {complete\r\n ? \"COMPLETE\"\r\n : statusLabel}\r\n </div>\r\n )}\r\n\r\n <p>\r\n {text.slice(\r\n 0,\r\n length\r\n )}\r\n\r\n {showCursor &&\r\n !complete && (\r\n <span\r\n className=\"fb-streaming-response__cursor\"\r\n aria-hidden=\"true\"\r\n />\r\n )}\r\n </p>\r\n </div>\r\n );\r\n}"]}
@@ -0,0 +1,195 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // src/motion/GlowCursor.tsx
5
+ function GlowCursor({
6
+ children,
7
+ color = "#825dff",
8
+ size = 110,
9
+ blur = 35,
10
+ opacity = 0.45,
11
+ followSpeed = 0.12,
12
+ className = ""
13
+ }) {
14
+ const [
15
+ point,
16
+ setPoint
17
+ ] = useState({
18
+ x: 0,
19
+ y: 0
20
+ });
21
+ return /* @__PURE__ */ jsxs(
22
+ "div",
23
+ {
24
+ className: `fb-glow-cursor ${className}`,
25
+ onMouseMove: (event) => {
26
+ const rect = event.currentTarget.getBoundingClientRect();
27
+ setPoint({
28
+ x: event.clientX - rect.left,
29
+ y: event.clientY - rect.top
30
+ });
31
+ },
32
+ children: [
33
+ /* @__PURE__ */ jsx(
34
+ "div",
35
+ {
36
+ className: "fb-glow-cursor__glow",
37
+ style: {
38
+ width: size,
39
+ height: size,
40
+ left: point.x,
41
+ top: point.y,
42
+ background: color,
43
+ filter: `blur(${blur}px)`,
44
+ opacity,
45
+ transitionDuration: `${followSpeed}s`
46
+ }
47
+ }
48
+ ),
49
+ /* @__PURE__ */ jsx("div", { className: "fb-glow-cursor__content", children })
50
+ ]
51
+ }
52
+ );
53
+ }
54
+ function MagneticCursor({
55
+ children,
56
+ color = "#b99cff",
57
+ size = 18,
58
+ ringSize = 44,
59
+ followSpeed = 0.14,
60
+ magneticStrength = 0.25,
61
+ className = "",
62
+ style,
63
+ ...props
64
+ }) {
65
+ const containerRef = useRef(
66
+ null
67
+ );
68
+ const [
69
+ point,
70
+ setPoint
71
+ ] = useState({
72
+ x: 0,
73
+ y: 0
74
+ });
75
+ const [
76
+ visible,
77
+ setVisible
78
+ ] = useState(false);
79
+ const [
80
+ hoveringTarget,
81
+ setHoveringTarget
82
+ ] = useState(false);
83
+ useEffect(() => {
84
+ const element = containerRef.current;
85
+ if (!element) {
86
+ return;
87
+ }
88
+ function handlePointerMove(event) {
89
+ const rect = element.getBoundingClientRect();
90
+ let x = event.clientX - rect.left;
91
+ let y = event.clientY - rect.top;
92
+ const target = event.target?.closest(
93
+ "[data-magnetic-target]"
94
+ );
95
+ if (target) {
96
+ const targetRect = target.getBoundingClientRect();
97
+ const centerX = targetRect.left + targetRect.width / 2;
98
+ const centerY = targetRect.top + targetRect.height / 2;
99
+ x += (centerX - event.clientX) * magneticStrength;
100
+ y += (centerY - event.clientY) * magneticStrength;
101
+ setHoveringTarget(
102
+ true
103
+ );
104
+ } else {
105
+ setHoveringTarget(
106
+ false
107
+ );
108
+ }
109
+ setPoint({
110
+ x,
111
+ y
112
+ });
113
+ setVisible(
114
+ true
115
+ );
116
+ }
117
+ function handlePointerLeave() {
118
+ setVisible(
119
+ false
120
+ );
121
+ setHoveringTarget(
122
+ false
123
+ );
124
+ }
125
+ element.addEventListener(
126
+ "pointermove",
127
+ handlePointerMove
128
+ );
129
+ element.addEventListener(
130
+ "pointerleave",
131
+ handlePointerLeave
132
+ );
133
+ return () => {
134
+ element.removeEventListener(
135
+ "pointermove",
136
+ handlePointerMove
137
+ );
138
+ element.removeEventListener(
139
+ "pointerleave",
140
+ handlePointerLeave
141
+ );
142
+ };
143
+ }, [
144
+ magneticStrength
145
+ ]);
146
+ return /* @__PURE__ */ jsxs(
147
+ "div",
148
+ {
149
+ ...props,
150
+ ref: containerRef,
151
+ className: `fb-magnetic-cursor ${className}`,
152
+ style: {
153
+ ...style,
154
+ cursor: "none"
155
+ },
156
+ children: [
157
+ /* @__PURE__ */ jsx("div", { className: "fb-magnetic-cursor__content", children }),
158
+ /* @__PURE__ */ jsx(
159
+ "span",
160
+ {
161
+ "aria-hidden": "true",
162
+ className: `fb-magnetic-cursor__ring ${visible ? "is-visible" : ""} ${hoveringTarget ? "is-targeting" : ""}`,
163
+ style: {
164
+ width: ringSize,
165
+ height: ringSize,
166
+ left: point.x,
167
+ top: point.y,
168
+ borderColor: color,
169
+ transitionDuration: `${followSpeed}s`
170
+ }
171
+ }
172
+ ),
173
+ /* @__PURE__ */ jsx(
174
+ "span",
175
+ {
176
+ "aria-hidden": "true",
177
+ className: `fb-magnetic-cursor__dot ${visible ? "is-visible" : ""}`,
178
+ style: {
179
+ width: size,
180
+ height: size,
181
+ left: point.x,
182
+ top: point.y,
183
+ background: color,
184
+ boxShadow: `0 0 ${size}px ${color}`
185
+ }
186
+ }
187
+ )
188
+ ]
189
+ }
190
+ );
191
+ }
192
+
193
+ export { GlowCursor, MagneticCursor };
194
+ //# sourceMappingURL=chunk-T7CC3CLH.js.map
195
+ //# sourceMappingURL=chunk-T7CC3CLH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/motion/GlowCursor.tsx","../src/motion/MagneticCursor.tsx"],"names":["useState","jsxs","jsx"],"mappings":"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","file":"chunk-T7CC3CLH.js","sourcesContent":["import {\r\n useState,\r\n} from \"react\";\r\n\r\nimport \"./GlowCursor.css\";\r\n\r\nexport interface GlowCursorProps {\r\n children?:\r\n React.ReactNode;\r\n\r\n color?:\r\n string;\r\n\r\n size?:\r\n number;\r\n\r\n blur?:\r\n number;\r\n\r\n opacity?:\r\n number;\r\n\r\n followSpeed?:\r\n number;\r\n\r\n className?:\r\n string;\r\n}\r\n\r\nexport function GlowCursor({\r\n children,\r\n\r\n color =\r\n \"#825dff\",\r\n\r\n size =\r\n 110,\r\n\r\n blur =\r\n 35,\r\n\r\n opacity =\r\n 0.45,\r\n\r\n followSpeed =\r\n 0.12,\r\n\r\n className =\r\n \"\",\r\n}: GlowCursorProps) {\r\n const [\r\n point,\r\n setPoint,\r\n ] =\r\n useState({\r\n x: 0,\r\n y: 0,\r\n });\r\n\r\n return (\r\n <div\r\n className={`fb-glow-cursor ${className}`}\r\n onMouseMove={(\r\n event\r\n ) => {\r\n const rect =\r\n event.currentTarget.getBoundingClientRect();\r\n\r\n setPoint({\r\n x:\r\n event.clientX -\r\n rect.left,\r\n\r\n y:\r\n event.clientY -\r\n rect.top,\r\n });\r\n }}\r\n >\r\n <div\r\n className=\"fb-glow-cursor__glow\"\r\n style={{\r\n width:\r\n size,\r\n\r\n height:\r\n size,\r\n\r\n left:\r\n point.x,\r\n\r\n top:\r\n point.y,\r\n\r\n background:\r\n color,\r\n\r\n filter:\r\n `blur(${blur}px)`,\r\n\r\n opacity,\r\n\r\n transitionDuration:\r\n `${followSpeed}s`,\r\n }}\r\n />\r\n\r\n <div className=\"fb-glow-cursor__content\">\r\n {children}\r\n </div>\r\n </div>\r\n );\r\n}","import {\r\n useEffect,\r\n useRef,\r\n useState,\r\n} from \"react\";\r\n\r\nimport type {\r\n HTMLAttributes,\r\n ReactNode,\r\n} from \"react\";\r\n\r\nimport \"./MagneticCursor.css\";\r\n\r\nexport interface MagneticCursorProps\r\n extends HTMLAttributes<HTMLDivElement> {\r\n children?: ReactNode;\r\n\r\n color?: string;\r\n\r\n size?: number;\r\n\r\n ringSize?: number;\r\n\r\n followSpeed?: number;\r\n\r\n magneticStrength?: number;\r\n\r\n className?: string;\r\n}\r\n\r\ninterface Point {\r\n x: number;\r\n y: number;\r\n}\r\n\r\nexport function MagneticCursor({\r\n children,\r\n\r\n color = \"#b99cff\",\r\n\r\n size = 18,\r\n\r\n ringSize = 44,\r\n\r\n followSpeed = 0.14,\r\n\r\n magneticStrength = 0.25,\r\n\r\n className = \"\",\r\n\r\n style,\r\n\r\n ...props\r\n}: MagneticCursorProps) {\r\n const containerRef =\r\n useRef<HTMLDivElement>(\r\n null\r\n );\r\n\r\n const [\r\n point,\r\n setPoint,\r\n ] =\r\n useState<Point>({\r\n x: 0,\r\n y: 0,\r\n });\r\n\r\n const [\r\n visible,\r\n setVisible,\r\n ] =\r\n useState(false);\r\n\r\n const [\r\n hoveringTarget,\r\n setHoveringTarget,\r\n ] =\r\n useState(false);\r\n\r\n useEffect(() => {\r\n const element =\r\n containerRef.current;\r\n\r\n if (!element) {\r\n return;\r\n }\r\n\r\n function handlePointerMove(\r\n event: PointerEvent\r\n ) {\r\n const rect =\r\n element!.getBoundingClientRect();\r\n\r\n let x =\r\n event.clientX -\r\n rect.left;\r\n\r\n let y =\r\n event.clientY -\r\n rect.top;\r\n\r\n const target =\r\n (\r\n event.target as\r\n HTMLElement | null\r\n )?.closest(\r\n \"[data-magnetic-target]\"\r\n ) as\r\n | HTMLElement\r\n | null;\r\n\r\n if (target) {\r\n const targetRect =\r\n target.getBoundingClientRect();\r\n\r\n const centerX =\r\n targetRect.left +\r\n targetRect.width /\r\n 2;\r\n\r\n const centerY =\r\n targetRect.top +\r\n targetRect.height /\r\n 2;\r\n\r\n x +=\r\n (\r\n centerX -\r\n event.clientX\r\n ) *\r\n magneticStrength;\r\n\r\n y +=\r\n (\r\n centerY -\r\n event.clientY\r\n ) *\r\n magneticStrength;\r\n\r\n setHoveringTarget(\r\n true\r\n );\r\n } else {\r\n setHoveringTarget(\r\n false\r\n );\r\n }\r\n\r\n setPoint({\r\n x,\r\n y,\r\n });\r\n\r\n setVisible(\r\n true\r\n );\r\n }\r\n\r\n function handlePointerLeave() {\r\n setVisible(\r\n false\r\n );\r\n\r\n setHoveringTarget(\r\n false\r\n );\r\n }\r\n\r\n element.addEventListener(\r\n \"pointermove\",\r\n handlePointerMove\r\n );\r\n\r\n element.addEventListener(\r\n \"pointerleave\",\r\n handlePointerLeave\r\n );\r\n\r\n return () => {\r\n element.removeEventListener(\r\n \"pointermove\",\r\n handlePointerMove\r\n );\r\n\r\n element.removeEventListener(\r\n \"pointerleave\",\r\n handlePointerLeave\r\n );\r\n };\r\n }, [\r\n magneticStrength,\r\n ]);\r\n\r\n return (\r\n <div\r\n {...props}\r\n ref={\r\n containerRef\r\n }\r\n className={`fb-magnetic-cursor ${className}`}\r\n style={{\r\n ...style,\r\n\r\n cursor:\r\n \"none\",\r\n }}\r\n >\r\n <div className=\"fb-magnetic-cursor__content\">\r\n {children}\r\n </div>\r\n\r\n <span\r\n aria-hidden=\"true\"\r\n className={`fb-magnetic-cursor__ring ${\r\n visible\r\n ? \"is-visible\"\r\n : \"\"\r\n } ${\r\n hoveringTarget\r\n ? \"is-targeting\"\r\n : \"\"\r\n }`}\r\n style={{\r\n width:\r\n ringSize,\r\n\r\n height:\r\n ringSize,\r\n\r\n left:\r\n point.x,\r\n\r\n top:\r\n point.y,\r\n\r\n borderColor:\r\n color,\r\n\r\n transitionDuration:\r\n `${followSpeed}s`,\r\n }}\r\n />\r\n\r\n <span\r\n aria-hidden=\"true\"\r\n className={`fb-magnetic-cursor__dot ${\r\n visible\r\n ? \"is-visible\"\r\n : \"\"\r\n }`}\r\n style={{\r\n width:\r\n size,\r\n\r\n height:\r\n size,\r\n\r\n left:\r\n point.x,\r\n\r\n top:\r\n point.y,\r\n\r\n background:\r\n color,\r\n\r\n boxShadow:\r\n `0 0 ${size}px ${color}`,\r\n }}\r\n />\r\n </div>\r\n );\r\n}"]}