@flikk/ui 1.0.0-beta.24 → 1.0.0-beta.26
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.
- package/dist/components/ai/CodeBlock/CodeBlock.theme.js +2 -2
- package/dist/components/ai/PromptInput/PromptInput.js +1 -0
- package/dist/components/ai/PromptInput/VoiceRecorder.js +1 -0
- package/dist/components/ai/StreamingResponse/MarkdownRenderer.js +1 -1
- package/dist/components/ai/TokenCounter/TokenCounter.theme.js +1 -1
- package/dist/components/charts/BarChart/BarChart.js +2 -2
- package/dist/components/charts/StackedBarChart/StackedBarChart.js +1 -1
- package/dist/components/charts/shared/BarRenderer/BarRenderer.js +2 -2
- package/dist/components/core/Accordion/Accordion.theme.js +2 -2
- package/dist/components/core/Breadcrumbs/Breadcrumbs.js +1 -1
- package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.js +2 -2
- package/dist/components/core/Button/Button.js +1 -1
- package/dist/components/core/Button/Button.theme.js +4 -4
- package/dist/components/core/Calendar/Calendar.js +1 -1
- package/dist/components/core/Calendar/CalendarMini/CalendarMini.js +4 -2
- package/dist/components/core/Calendar/CalendarMini/CalendarMini.theme.js +2 -2
- package/dist/components/core/ContextMenu/ContextMenu.theme.js +1 -1
- package/dist/components/core/Drawer/Drawer.theme.js +2 -2
- package/dist/components/core/Dropdown/Dropdown.theme.js +2 -2
- package/dist/components/core/Empty/Empty.js +1 -1
- package/dist/components/core/Empty/Empty.theme.js +1 -1
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.d.ts +14 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.js +28 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.theme.d.ts +6 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.theme.js +8 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.types.d.ts +32 -0
- package/dist/components/core/ImagePlaceholder/index.d.ts +2 -0
- package/dist/components/core/Link/Link.js +1 -1
- package/dist/components/core/Message/Message.theme.js +1 -1
- package/dist/components/core/Modal/Modal.theme.js +1 -1
- package/dist/components/core/NavItem/NavItem.d.ts +1 -1
- package/dist/components/core/NavItem/NavItem.js +171 -68
- package/dist/components/core/NavItem/NavItem.theme.js +20 -12
- package/dist/components/core/NavItem/NavItem.types.d.ts +17 -39
- package/dist/components/core/Pagination/Pagination.theme.js +3 -3
- package/dist/components/core/Popover/Popover.theme.js +1 -1
- package/dist/components/core/Progress/Progress.theme.js +1 -1
- package/dist/components/core/ScrollArea/ScrollArea.js +2 -2
- package/dist/components/core/ScrollArea/ScrollArea.theme.js +1 -1
- package/dist/components/core/Segmented/Segmented.js +8 -6
- package/dist/components/core/Segmented/Segmented.theme.js +3 -3
- package/dist/components/core/Sidebar/Sidebar.d.ts +1 -1
- package/dist/components/core/Sidebar/Sidebar.js +42 -13
- package/dist/components/core/Sidebar/Sidebar.theme.js +5 -7
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +33 -4
- package/dist/components/core/Sidebar/SidebarContent.js +6 -6
- package/dist/components/core/Sidebar/SidebarContext.d.ts +17 -16
- package/dist/components/core/Sidebar/SidebarContext.js +42 -19
- package/dist/components/core/Sidebar/SidebarFooter.js +4 -6
- package/dist/components/core/Sidebar/SidebarHeader.js +4 -9
- package/dist/components/core/Sidebar/SidebarLogo.js +3 -7
- package/dist/components/core/Sidebar/SidebarNav.js +4 -3
- package/dist/components/core/Sidebar/SidebarNavGroup.js +4 -7
- package/dist/components/core/Sidebar/SidebarToggle.js +6 -1
- package/dist/components/core/Sidebar/SidebarUserProfile.js +33 -17
- package/dist/components/core/Sidebar/index.d.ts +2 -3
- package/dist/components/core/Skeleton/Skeleton.theme.js +1 -1
- package/dist/components/core/Spinner/Spinner.theme.js +1 -1
- package/dist/components/core/Tabs/Tabs.js +6 -1
- package/dist/components/core/Tabs/Tabs.types.d.ts +2 -0
- package/dist/components/core/Tabs/TabsBody.js +5 -3
- package/dist/components/core/Tag/Tag.theme.js +1 -1
- package/dist/components/core/index.d.ts +1 -0
- package/dist/components/core/index.js +2 -2
- package/dist/components/data-display/GanttChart/GanttTableGroupRow.js +1 -1
- package/dist/components/data-display/KPI/KPI.js +13 -3
- package/dist/components/data-display/KPI/KPI.theme.js +3 -3
- package/dist/components/data-display/Metric/Metric.theme.js +3 -3
- package/dist/components/data-display/Table/Table.js +4 -3
- package/dist/components/data-display/Table/Table.theme.js +11 -1
- package/dist/components/data-display/Table/Table.types.d.ts +15 -0
- package/dist/components/data-display/Table/TableColumnManager.js +1 -1
- package/dist/components/data-display/Table/index.d.ts +2 -2
- package/dist/components/data-display/Timeline/Timeline.animations.d.ts +0 -7
- package/dist/components/data-display/Timeline/Timeline.animations.js +1 -27
- package/dist/components/data-display/Timeline/Timeline.js +3 -4
- package/dist/components/data-display/Timeline/Timeline.types.d.ts +0 -13
- package/dist/components/data-display/Timeline/TimelineContent.js +3 -7
- package/dist/components/data-display/Timeline/TimelineItem.js +4 -17
- package/dist/components/data-display/Timeline/index.d.ts +2 -2
- package/dist/components/effects/Aurora/Aurora.js +79 -40
- package/dist/components/effects/InteractiveCharacters/InteractiveCharacters.js +3 -3
- package/dist/components/effects/InteractiveCharacters/characters/BlobCharacter.js +13 -4
- package/dist/components/effects/InteractiveCharacters/characters/SmallCharacter.js +13 -4
- package/dist/components/effects/InteractiveCharacters/characters/TallCharacter.js +14 -4
- package/dist/components/effects/MorphingText/MorphingText.js +40 -7
- package/dist/components/effects/StripePattern/StripePattern.d.ts +18 -0
- package/dist/components/effects/StripePattern/StripePattern.js +54 -0
- package/dist/components/effects/StripePattern/StripePattern.types.d.ts +53 -0
- package/dist/components/effects/StripePattern/index.d.ts +2 -0
- package/dist/components/effects/index.d.ts +9 -0
- package/dist/components/effects/index.js +4 -0
- package/dist/components/forms/Checkbox/Checkbox.theme.js +1 -1
- package/dist/components/forms/CronInput/CronInput.theme.js +16 -16
- package/dist/components/forms/DatePicker/DatePickerTrigger.js +1 -1
- package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +1 -1
- package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +1 -1
- package/dist/components/forms/FileUpload/FileUpload.js +1 -1
- package/dist/components/forms/FileUpload/FileUploadProgress.js +27 -20
- package/dist/components/forms/FileUpload/FileUploadProgress.theme.js +2 -2
- package/dist/components/forms/Input/Input.theme.js +9 -11
- package/dist/components/forms/Mention/Mention.theme.js +3 -3
- package/dist/components/forms/Radio/Radio.theme.js +1 -1
- package/dist/components/forms/RichTextEditor/RichTextEditor.d.ts +18 -0
- package/dist/components/forms/RichTextEditor/RichTextEditor.js +1231 -236
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.d.ts +53 -1
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +99 -25
- package/dist/components/forms/RichTextEditor/RichTextEditor.types.d.ts +42 -3
- package/dist/components/forms/RichTextEditor/index.d.ts +2 -2
- package/dist/components/forms/Select/Select.js +2 -2
- package/dist/components/forms/Slider/Slider.theme.js +1 -1
- package/dist/components/forms/Switch/Switch.theme.js +1 -1
- package/dist/components/forms/TimePicker/TimePicker.theme.js +2 -2
- package/dist/components/forms/TimePicker/TimePickerContent.js +1 -0
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
- package/dist/components/forms/forms.theme.js +12 -12
- package/dist/components/forms/index.js +1 -0
- package/dist/components/layout/FormLayout/FormLayout.js +6 -4
- package/dist/components/layout/FormLayout/FormLayout.types.d.ts +3 -3
- package/dist/components/layout/FormLayout/FormLayoutSection.js +6 -1
- package/dist/components/layout/PageLayout/PageLayout.d.ts +2 -2
- package/dist/components/layout/PageLayout/PageLayout.js +23 -5
- package/dist/components/layout/PageLayout/PageLayout.theme.d.ts +2 -1
- package/dist/components/layout/PageLayout/PageLayout.theme.js +35 -6
- package/dist/components/layout/PageLayout/PageLayout.types.d.ts +45 -0
- package/dist/components/layout/PageLayout/PageLayoutContent.js +6 -4
- package/dist/components/layout/PageLayout/PageLayoutHeader.js +3 -3
- package/dist/components/layout/PageLayout/PageLayoutSidebar.js +5 -3
- package/dist/components/layout/PageLayout/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/styles/theme.css +127 -92
- package/dist/components/core/Sidebar/SidebarSubmenu.d.ts +0 -7
- package/dist/components/core/Sidebar/SidebarSubmenu.js +0 -12
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { jsx } from 'react/jsx-runtime';
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useReducedMotion, motion } from 'motion/react';
|
|
3
3
|
import { cn } from '../../../utils/cn.js';
|
|
4
4
|
|
|
5
5
|
const DEFAULT_COLORS = ['#3b82f6', '#8b5cf6', '#ec4899', '#06b6d4'];
|
|
6
|
+
const clamp = (value, min, max) => {
|
|
7
|
+
if (Number.isNaN(value))
|
|
8
|
+
return min;
|
|
9
|
+
return Math.min(Math.max(value, min), max);
|
|
10
|
+
};
|
|
6
11
|
/**
|
|
7
12
|
* Each blob follows a unique organic path using keyframed x/y translations
|
|
8
13
|
* and scale changes. The durations are intentionally varied so the blobs
|
|
@@ -37,16 +42,32 @@ const blobAnimations = [
|
|
|
37
42
|
scale: [1, 0.95, 1.1, 0.9, 1],
|
|
38
43
|
baseDuration: 31,
|
|
39
44
|
},
|
|
45
|
+
{
|
|
46
|
+
// Blob 4 -- slow vertical ribbon sweep
|
|
47
|
+
x: ['0%', '12%', '-18%', '20%', '0%'],
|
|
48
|
+
y: ['0%', '-35%', '-5%', '24%', '0%'],
|
|
49
|
+
scale: [1, 1.25, 0.95, 1.1, 1],
|
|
50
|
+
baseDuration: 29,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
// Blob 5 -- small counter drift to keep dense palettes from syncing
|
|
54
|
+
x: ['0%', '-18%', '10%', '28%', '0%'],
|
|
55
|
+
y: ['0%', '12%', '-22%', '-8%', '0%'],
|
|
56
|
+
scale: [1, 0.88, 1.18, 0.98, 1],
|
|
57
|
+
baseDuration: 34,
|
|
58
|
+
},
|
|
40
59
|
];
|
|
41
60
|
/**
|
|
42
61
|
* Positions each blob at a different quadrant of the container so they
|
|
43
62
|
* begin spread out. Additional blobs beyond 4 cycle through positions.
|
|
44
63
|
*/
|
|
45
64
|
const blobPositions = [
|
|
46
|
-
{ top: '
|
|
47
|
-
{ top: '
|
|
48
|
-
{ bottom: '
|
|
49
|
-
{ bottom: '
|
|
65
|
+
{ top: '8%', left: '12%' },
|
|
66
|
+
{ top: '0%', right: '8%' },
|
|
67
|
+
{ bottom: '8%', left: '18%' },
|
|
68
|
+
{ bottom: '0%', right: '12%' },
|
|
69
|
+
{ top: '34%', left: '36%' },
|
|
70
|
+
{ bottom: '28%', right: '34%' },
|
|
50
71
|
];
|
|
51
72
|
/**
|
|
52
73
|
* Aurora creates an animated multi-color gradient background effect.
|
|
@@ -71,49 +92,67 @@ const blobPositions = [
|
|
|
71
92
|
*/
|
|
72
93
|
const Aurora = ({ colors = DEFAULT_COLORS, blur = 80, speed = 1, opacity = 0.3, size = 1, className, style, ...props }) => {
|
|
73
94
|
const shouldReduceMotion = useReducedMotion();
|
|
95
|
+
const safeColors = colors.length > 0 ? colors : DEFAULT_COLORS;
|
|
96
|
+
const safeBlur = Math.max(0, blur);
|
|
97
|
+
const safeSpeed = Math.max(0.1, speed);
|
|
98
|
+
const safeOpacity = clamp(opacity, 0, 1);
|
|
99
|
+
const safeSize = Math.max(0.1, size);
|
|
74
100
|
// Compute base blob dimension from the size multiplier.
|
|
75
101
|
// Using percentage-based sizing so it scales with the container.
|
|
76
|
-
const blobDimension = `${40 *
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
const blobDimension = `${40 * safeSize}%`;
|
|
103
|
+
const bleed = safeBlur * 1.5;
|
|
104
|
+
const wash = safeColors
|
|
105
|
+
.slice(0, 4)
|
|
106
|
+
.map((color, index) => {
|
|
107
|
+
const stops = [
|
|
108
|
+
'20% 30%',
|
|
109
|
+
'80% 20%',
|
|
110
|
+
'30% 80%',
|
|
111
|
+
'75% 75%',
|
|
112
|
+
];
|
|
113
|
+
return `radial-gradient(circle at ${stops[index]}, ${color}55, transparent 42%)`;
|
|
114
|
+
})
|
|
115
|
+
.join(', ');
|
|
116
|
+
return (jsxs("div", { className: cn('absolute inset-0 overflow-hidden pointer-events-none', className), style: {
|
|
80
117
|
...style,
|
|
81
|
-
}, "aria-hidden": "true", ...props, children:
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
width: blobDimension,
|
|
91
|
-
height: blobDimension,
|
|
92
|
-
borderRadius: '50%',
|
|
93
|
-
background: `radial-gradient(circle at center, ${color}, transparent 70%)`,
|
|
94
|
-
mixBlendMode: 'screen',
|
|
95
|
-
} }, index));
|
|
96
|
-
}
|
|
97
|
-
return (jsx(motion.div, { animate: {
|
|
98
|
-
x: anim.x,
|
|
99
|
-
y: anim.y,
|
|
100
|
-
scale: anim.scale,
|
|
101
|
-
}, transition: {
|
|
102
|
-
duration,
|
|
103
|
-
repeat: Infinity,
|
|
104
|
-
repeatType: 'loop',
|
|
105
|
-
ease: 'easeInOut',
|
|
106
|
-
}, style: {
|
|
118
|
+
}, "aria-hidden": "true", ...props, children: [jsx("div", { className: "absolute inset-0", style: {
|
|
119
|
+
background: wash,
|
|
120
|
+
opacity: safeOpacity * 0.65,
|
|
121
|
+
mixBlendMode: 'screen',
|
|
122
|
+
} }), safeColors.map((color, index) => {
|
|
123
|
+
const anim = blobAnimations[index % blobAnimations.length];
|
|
124
|
+
const position = blobPositions[index % blobPositions.length];
|
|
125
|
+
const duration = anim.baseDuration / safeSpeed;
|
|
126
|
+
const blobStyle = {
|
|
107
127
|
position: 'absolute',
|
|
108
128
|
...position,
|
|
109
129
|
width: blobDimension,
|
|
110
130
|
height: blobDimension,
|
|
111
|
-
borderRadius: '
|
|
112
|
-
background: `radial-gradient(circle at center, ${color}, transparent
|
|
131
|
+
borderRadius: '9999px',
|
|
132
|
+
background: `radial-gradient(circle at center, ${color}, transparent 68%)`,
|
|
133
|
+
filter: `blur(${safeBlur}px)`,
|
|
113
134
|
mixBlendMode: 'screen',
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
135
|
+
opacity: safeOpacity,
|
|
136
|
+
margin: -bleed,
|
|
137
|
+
};
|
|
138
|
+
// When reduced motion is preferred, render static blobs with no animation.
|
|
139
|
+
if (shouldReduceMotion) {
|
|
140
|
+
return (jsx("div", { style: blobStyle }, index));
|
|
141
|
+
}
|
|
142
|
+
return (jsx(motion.div, { animate: {
|
|
143
|
+
x: anim.x,
|
|
144
|
+
y: anim.y,
|
|
145
|
+
scale: anim.scale,
|
|
146
|
+
}, transition: {
|
|
147
|
+
duration,
|
|
148
|
+
repeat: Infinity,
|
|
149
|
+
repeatType: 'loop',
|
|
150
|
+
ease: 'easeInOut',
|
|
151
|
+
}, style: {
|
|
152
|
+
...blobStyle,
|
|
153
|
+
willChange: 'transform',
|
|
154
|
+
} }, index));
|
|
155
|
+
})] }));
|
|
117
156
|
};
|
|
118
157
|
|
|
119
158
|
export { Aurora };
|
|
@@ -15,7 +15,7 @@ const sizeMap = {
|
|
|
15
15
|
md: 'w-[320px]',
|
|
16
16
|
lg: 'w-[400px]',
|
|
17
17
|
};
|
|
18
|
-
/** Click reaction states per character */
|
|
18
|
+
/** Click reaction states per abstract character */
|
|
19
19
|
const CLICK_REACTIONS = {
|
|
20
20
|
blob: 'surprised',
|
|
21
21
|
tall: 'love',
|
|
@@ -47,7 +47,7 @@ const InteractiveCharacters = ({ state = 'idle', trackMouse = true, size = 'md',
|
|
|
47
47
|
const smallState = clickedChar === 'small' ? CLICK_REACTIONS.small : state;
|
|
48
48
|
const isTrackingEnabled = trackMouse && state !== 'password' && !shouldReduceMotion;
|
|
49
49
|
const isIdleAnimated = !shouldReduceMotion;
|
|
50
|
-
// Mouse tracking
|
|
50
|
+
// Mouse tracking: each abstract form gets a slightly different response.
|
|
51
51
|
const blobEye = useMouseTracking({
|
|
52
52
|
containerRef: svgRef,
|
|
53
53
|
enabled: isTrackingEnabled,
|
|
@@ -63,7 +63,7 @@ const InteractiveCharacters = ({ state = 'idle', trackMouse = true, size = 'md',
|
|
|
63
63
|
enabled: isTrackingEnabled,
|
|
64
64
|
...springConfigs.small,
|
|
65
65
|
});
|
|
66
|
-
// Idle sway/breathing/head
|
|
66
|
+
// Idle sway/breathing/head movement runs at different rates and phase offsets.
|
|
67
67
|
const blobIdle = useIdleAnimation({
|
|
68
68
|
enabled: isIdleAnimated,
|
|
69
69
|
swayAmount: 1.5,
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useId } from 'react';
|
|
2
3
|
import { useTransform, motion } from 'motion/react';
|
|
3
4
|
import { eyelidVariants, clickTransition, bodyTransition, blobBodyVariants, blobMouthPaths } from '../InteractiveCharacters.animations.js';
|
|
4
5
|
|
|
5
|
-
const LEFT_EYE = { cx: 32, cy: -
|
|
6
|
-
const RIGHT_EYE = { cx:
|
|
6
|
+
const LEFT_EYE = { cx: 32, cy: -58 };
|
|
7
|
+
const RIGHT_EYE = { cx: 62, cy: -58 };
|
|
7
8
|
const CLICK_STATES = new Set(['surprised', 'love', 'excited']);
|
|
8
9
|
const BlobCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
10
|
+
const id = useId().replace(/:/g, '');
|
|
11
|
+
const leftClipId = `${id}-aura-eye-l`;
|
|
12
|
+
const rightClipId = `${id}-aura-eye-r`;
|
|
13
|
+
const bodyGradientId = `${id}-aura-body`;
|
|
9
14
|
const leftPupilX = useTransform(eyeOffset.x, (v) => LEFT_EYE.cx + v);
|
|
10
15
|
const leftPupilY = useTransform(eyeOffset.y, (v) => LEFT_EYE.cy + v);
|
|
11
16
|
const rightPupilX = useTransform(eyeOffset.x, (v) => RIGHT_EYE.cx + v);
|
|
@@ -24,13 +29,17 @@ const BlobCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
|
24
29
|
x: idle.sway,
|
|
25
30
|
y: idle.bob,
|
|
26
31
|
scale: idle.breath,
|
|
27
|
-
}, onClick: onClick, children: [jsx("
|
|
32
|
+
}, onClick: onClick, children: [jsx("defs", { children: jsxs("linearGradient", { id: bodyGradientId, x1: "15", y1: "-105", x2: "92", y2: "-5", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { stopColor: "#16B8A6" }), jsx("stop", { offset: "1", stopColor: "#0F766E" })] }) }), jsx(motion.path, { d: "M 19 -99 C 40 -113 78 -101 92 -75 C 108 -45 91 -10 58 -1 C 26 8 -2 -13 -5 -47 C -8 -72 3 -89 19 -99 Z", fill: `url(#${bodyGradientId})`, animate: {
|
|
33
|
+
d: state === 'surprised'
|
|
34
|
+
? 'M 19 -105 C 42 -121 82 -106 97 -78 C 115 -43 94 -6 57 1 C 21 8 -9 -17 -10 -52 C -11 -79 2 -96 19 -105 Z'
|
|
35
|
+
: 'M 19 -99 C 40 -113 78 -101 92 -75 C 108 -45 91 -10 58 -1 C 26 8 -2 -13 -5 -47 C -8 -72 3 -89 19 -99 Z',
|
|
36
|
+
}, transition: transition }), jsx("path", { d: "M 72 -91 C 91 -78 98 -52 88 -30", fill: "none", stroke: "#7DD3C7", strokeWidth: 5, strokeLinecap: "round", opacity: 0.38 }), jsx("ellipse", { cx: 11, cy: -31, rx: 8, ry: 12, fill: "#5EEAD4", opacity: 0.34, transform: "rotate(-24 11 -31)" }), jsxs(motion.g, { style: {
|
|
28
37
|
originX: '50px',
|
|
29
38
|
originY: '-50px',
|
|
30
39
|
rotate: combinedHeadRotate,
|
|
31
40
|
x: combinedHeadX,
|
|
32
41
|
y: combinedHeadY,
|
|
33
|
-
}, children: [jsxs("g", { children: [jsx("clipPath", { id:
|
|
42
|
+
}, children: [jsxs("g", { children: [jsx("clipPath", { id: leftClipId, children: jsx("ellipse", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, rx: 14, ry: 16 }) }), jsx("ellipse", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, rx: 12, ry: 14, fill: "#F8FAFC" }), jsx(motion.circle, { cx: leftPupilX, cy: leftPupilY, r: 7, fill: "#122A33", clipPath: `url(#${leftClipId})` }), jsx("circle", { cx: LEFT_EYE.cx + 4, cy: LEFT_EYE.cy - 5.5, r: 3, fill: "white", opacity: 0.95 }), jsx("circle", { cx: LEFT_EYE.cx - 3, cy: LEFT_EYE.cy + 4, r: 1.5, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: LEFT_EYE.cx - 12, y: LEFT_EYE.cy - 14, width: 24, height: 28, fill: "#16B8A6", rx: 3, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsxs("g", { children: [jsx("clipPath", { id: rightClipId, children: jsx("ellipse", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, rx: 14, ry: 16 }) }), jsx("ellipse", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, rx: 12, ry: 14, fill: "#F8FAFC" }), jsx(motion.circle, { cx: rightPupilX, cy: rightPupilY, r: 7, fill: "#122A33", clipPath: `url(#${rightClipId})` }), jsx("circle", { cx: RIGHT_EYE.cx + 4, cy: RIGHT_EYE.cy - 5.5, r: 3, fill: "white", opacity: 0.95 }), jsx("circle", { cx: RIGHT_EYE.cx - 3, cy: RIGHT_EYE.cy + 4, r: 1.5, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: RIGHT_EYE.cx - 12, y: RIGHT_EYE.cy - 14, width: 24, height: 28, fill: "#16B8A6", rx: 3, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsx("ellipse", { cx: 20, cy: -37, rx: 6, ry: 3.5, fill: "#99F6E4", opacity: state === 'surprised' ? 0.72 : 0.45 }), jsx("ellipse", { cx: 76, cy: -36, rx: 6, ry: 3.5, fill: "#99F6E4", opacity: state === 'surprised' ? 0.72 : 0.45 }), jsx(motion.path, { d: blobMouthPaths[state], fill: "none", stroke: "#122A33", strokeWidth: 2.4, strokeLinecap: "round", animate: { d: blobMouthPaths[state] }, transition: transition, transform: "translate(47, -30)" })] })] }));
|
|
34
43
|
};
|
|
35
44
|
|
|
36
45
|
export { BlobCharacter };
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useId } from 'react';
|
|
2
3
|
import { useTransform, motion } from 'motion/react';
|
|
3
4
|
import { eyelidVariants, clickTransition, bodyTransition, smallBodyVariants, smallMouthPaths } from '../InteractiveCharacters.animations.js';
|
|
4
5
|
|
|
5
|
-
const LEFT_EYE = { cx: -
|
|
6
|
-
const RIGHT_EYE = { cx:
|
|
6
|
+
const LEFT_EYE = { cx: -13, cy: -34 };
|
|
7
|
+
const RIGHT_EYE = { cx: 12, cy: -34 };
|
|
7
8
|
const CLICK_STATES = new Set(['surprised', 'love', 'excited']);
|
|
8
9
|
const SmallCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
10
|
+
const id = useId().replace(/:/g, '');
|
|
11
|
+
const leftClipId = `${id}-glyph-eye-l`;
|
|
12
|
+
const rightClipId = `${id}-glyph-eye-r`;
|
|
13
|
+
const bodyGradientId = `${id}-glyph-body`;
|
|
9
14
|
const leftPupilX = useTransform(eyeOffset.x, (v) => LEFT_EYE.cx + v);
|
|
10
15
|
const leftPupilY = useTransform(eyeOffset.y, (v) => LEFT_EYE.cy + v);
|
|
11
16
|
const rightPupilX = useTransform(eyeOffset.x, (v) => RIGHT_EYE.cx + v);
|
|
@@ -24,13 +29,17 @@ const SmallCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
|
24
29
|
x: idle.sway,
|
|
25
30
|
y: idle.bob,
|
|
26
31
|
scale: idle.breath,
|
|
27
|
-
}, onClick: onClick, children: [jsx("
|
|
32
|
+
}, onClick: onClick, children: [jsx("defs", { children: jsxs("linearGradient", { id: bodyGradientId, x1: "-48", y1: "-73", x2: "38", y2: "0", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { stopColor: "#FDA4AF" }), jsx("stop", { offset: "1", stopColor: "#FB7185" })] }) }), jsx(motion.path, { d: "M -38 -28 C -43 -57 -18 -78 13 -67 C 39 -58 50 -27 31 -8 C 13 10 -29 3 -38 -28 Z", fill: `url(#${bodyGradientId})`, animate: {
|
|
33
|
+
d: state === 'excited'
|
|
34
|
+
? 'M -40 -31 C -47 -63 -17 -85 17 -72 C 45 -61 56 -27 33 -5 C 12 15 -31 5 -40 -31 Z'
|
|
35
|
+
: 'M -38 -28 C -43 -57 -18 -78 13 -67 C 39 -58 50 -27 31 -8 C 13 10 -29 3 -38 -28 Z',
|
|
36
|
+
}, transition: transition }), jsx("path", { d: "M 20 -59 C 38 -47 42 -26 31 -13", fill: "none", stroke: "#FFE4E6", strokeWidth: 4, strokeLinecap: "round", opacity: 0.5 }), jsx("circle", { cx: -44, cy: -61, r: 7, fill: "#38BDF8", opacity: state === 'excited' ? 0.95 : 0.65 }), jsxs(motion.g, { style: {
|
|
28
37
|
originX: '0px',
|
|
29
38
|
originY: '-30px',
|
|
30
39
|
rotate: combinedHeadRotate,
|
|
31
40
|
x: combinedHeadX,
|
|
32
41
|
y: combinedHeadY,
|
|
33
|
-
}, children: [jsxs("g", { children: [jsx("clipPath", { id:
|
|
42
|
+
}, children: [jsxs("g", { children: [jsx("clipPath", { id: leftClipId, children: jsx("circle", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, r: 10 }) }), jsx("circle", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, r: 8.5, fill: "#FFF7ED" }), jsx(motion.circle, { cx: leftPupilX, cy: leftPupilY, r: 5.5, fill: "#253040", clipPath: `url(#${leftClipId})` }), jsx("circle", { cx: LEFT_EYE.cx + 3, cy: LEFT_EYE.cy - 4, r: 2.2, fill: "white", opacity: 0.95 }), jsx("circle", { cx: LEFT_EYE.cx - 2, cy: LEFT_EYE.cy + 3, r: 1.1, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: LEFT_EYE.cx - 8.5, y: LEFT_EYE.cy - 8.5, width: 17, height: 17, fill: "#FB7B8C", rx: 2, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsxs("g", { children: [jsx("clipPath", { id: rightClipId, children: jsx("circle", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, r: 10 }) }), jsx("circle", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, r: 8.5, fill: "#FFF7ED" }), jsx(motion.circle, { cx: rightPupilX, cy: rightPupilY, r: 5.5, fill: "#253040", clipPath: `url(#${rightClipId})` }), jsx("circle", { cx: RIGHT_EYE.cx + 3, cy: RIGHT_EYE.cy - 4, r: 2.2, fill: "white", opacity: 0.95 }), jsx("circle", { cx: RIGHT_EYE.cx - 2, cy: RIGHT_EYE.cy + 3, r: 1.1, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: RIGHT_EYE.cx - 8.5, y: RIGHT_EYE.cy - 8.5, width: 17, height: 17, fill: "#FB7B8C", rx: 2, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsx("ellipse", { cx: -25, cy: -20, rx: 5, ry: 3, fill: "#FFE4E6", opacity: state === 'excited' ? 0.9 : 0.55 }), jsx("ellipse", { cx: 23, cy: -20, rx: 5, ry: 3, fill: "#FFE4E6", opacity: state === 'excited' ? 0.9 : 0.55 }), jsx(motion.path, { d: smallMouthPaths[state], fill: "none", stroke: "#1E293B", strokeWidth: 2, strokeLinecap: "round", animate: { d: smallMouthPaths[state] }, transition: transition, transform: "translate(0, -17)" })] })] }));
|
|
34
43
|
};
|
|
35
44
|
|
|
36
45
|
export { SmallCharacter };
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useId } from 'react';
|
|
2
3
|
import { useTransform, motion } from 'motion/react';
|
|
3
4
|
import { eyelidVariants, clickTransition, bodyTransition, tallBodyVariants, eyebrowVariants, tallMouthPaths } from '../InteractiveCharacters.animations.js';
|
|
4
5
|
|
|
5
|
-
const LEFT_EYE = { cx: -12, cy: -
|
|
6
|
-
const RIGHT_EYE = { cx: 12, cy: -
|
|
6
|
+
const LEFT_EYE = { cx: -12, cy: -73 };
|
|
7
|
+
const RIGHT_EYE = { cx: 12, cy: -73 };
|
|
7
8
|
const CLICK_STATES = new Set(['surprised', 'love', 'excited']);
|
|
8
9
|
const TallCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
10
|
+
const id = useId().replace(/:/g, '');
|
|
11
|
+
const leftClipId = `${id}-stack-eye-l`;
|
|
12
|
+
const rightClipId = `${id}-stack-eye-r`;
|
|
13
|
+
const topGradientId = `${id}-stack-top`;
|
|
14
|
+
const baseGradientId = `${id}-stack-base`;
|
|
9
15
|
const leftPupilX = useTransform(eyeOffset.x, (v) => LEFT_EYE.cx + v);
|
|
10
16
|
const leftPupilY = useTransform(eyeOffset.y, (v) => LEFT_EYE.cy + v);
|
|
11
17
|
const rightPupilX = useTransform(eyeOffset.x, (v) => RIGHT_EYE.cx + v);
|
|
@@ -25,13 +31,17 @@ const TallCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
|
25
31
|
x: idle.sway,
|
|
26
32
|
y: idle.bob,
|
|
27
33
|
scale: idle.breath,
|
|
28
|
-
}, onClick: onClick, children: [
|
|
34
|
+
}, onClick: onClick, children: [jsxs("defs", { children: [jsxs("linearGradient", { id: topGradientId, x1: "-33", y1: "-128", x2: "34", y2: "-54", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { stopColor: "#A5B4FC" }), jsx("stop", { offset: "1", stopColor: "#6366F1" })] }), jsxs("linearGradient", { id: baseGradientId, x1: "-46", y1: "-69", x2: "45", y2: "0", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { stopColor: "#67E8F9" }), jsx("stop", { offset: "1", stopColor: "#14B8A6" })] })] }), jsx(motion.path, { d: "M -20 -132 C 8 -146 37 -124 34 -94 C 32 -70 8 -57 -16 -65 C -42 -74 -47 -117 -20 -132 Z", fill: `url(#${topGradientId})`, animate: {
|
|
35
|
+
d: state === 'love'
|
|
36
|
+
? 'M -22 -136 C 9 -153 42 -126 37 -92 C 34 -66 8 -53 -18 -63 C -48 -74 -51 -120 -22 -136 Z'
|
|
37
|
+
: 'M -20 -132 C 8 -146 37 -124 34 -94 C 32 -70 8 -57 -16 -65 C -42 -74 -47 -117 -20 -132 Z',
|
|
38
|
+
}, transition: transition }), jsx("path", { d: "M -39 -63 C -15 -86 31 -78 43 -47 C 55 -13 28 9 -10 4 C -47 -1 -66 -35 -39 -63 Z", fill: `url(#${baseGradientId})` }), jsx("path", { d: "M -25 -121 C -6 -132 15 -129 25 -112", fill: "none", stroke: "#E0E7FF", strokeLinecap: "round", strokeWidth: 4, opacity: 0.48 }), jsxs(motion.g, { style: {
|
|
29
39
|
originX: '0px',
|
|
30
40
|
originY: '-70px',
|
|
31
41
|
rotate: combinedHeadRotate,
|
|
32
42
|
x: combinedHeadX,
|
|
33
43
|
y: combinedHeadY,
|
|
34
|
-
}, children: [jsx(motion.line, { x1: -
|
|
44
|
+
}, children: [jsx(motion.line, { x1: -23, y1: -94, x2: -5, y2: -94, stroke: "#1E293B", strokeWidth: 2.5, strokeLinecap: "round", animate: { rotate: eyebrow.rotate, y: eyebrow.y }, transition: transition, style: { originX: '50%', originY: '50%', transformBox: 'fill-box' } }), jsx(motion.line, { x1: 5, y1: -94, x2: 23, y2: -94, stroke: "#1E293B", strokeWidth: 2.5, strokeLinecap: "round", animate: { rotate: -eyebrow.rotate, y: eyebrow.y }, transition: transition, style: { originX: '50%', originY: '50%', transformBox: 'fill-box' } }), jsxs("g", { children: [jsx("clipPath", { id: leftClipId, children: jsx("ellipse", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, rx: 11, ry: 14 }) }), jsx("ellipse", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, rx: 10, ry: 12, fill: "#F8FAFC" }), jsx(motion.circle, { cx: leftPupilX, cy: leftPupilY, r: 6.5, fill: "#1E293B", clipPath: `url(#${leftClipId})` }), jsx("circle", { cx: LEFT_EYE.cx + 3.5, cy: LEFT_EYE.cy - 5, r: 2.5, fill: "white", opacity: 0.95 }), jsx("circle", { cx: LEFT_EYE.cx - 2.5, cy: LEFT_EYE.cy + 3, r: 1.2, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: LEFT_EYE.cx - 10, y: LEFT_EYE.cy - 12, width: 20, height: 24, fill: "#7C83F3", rx: 3, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsxs("g", { children: [jsx("clipPath", { id: rightClipId, children: jsx("ellipse", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, rx: 11, ry: 14 }) }), jsx("ellipse", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, rx: 10, ry: 12, fill: "#F8FAFC" }), jsx(motion.circle, { cx: rightPupilX, cy: rightPupilY, r: 6.5, fill: "#1E293B", clipPath: `url(#${rightClipId})` }), jsx("circle", { cx: RIGHT_EYE.cx + 3.5, cy: RIGHT_EYE.cy - 5, r: 2.5, fill: "white", opacity: 0.95 }), jsx("circle", { cx: RIGHT_EYE.cx - 2.5, cy: RIGHT_EYE.cy + 3, r: 1.2, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: RIGHT_EYE.cx - 10, y: RIGHT_EYE.cy - 12, width: 20, height: 24, fill: "#7C83F3", rx: 3, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsx("ellipse", { cx: -25, cy: -57, rx: 6, ry: 3.5, fill: "#C4B5FD", opacity: state === 'love' ? 0.75 : 0.45 }), jsx("ellipse", { cx: 25, cy: -57, rx: 6, ry: 3.5, fill: "#C4B5FD", opacity: state === 'love' ? 0.75 : 0.45 }), jsx(motion.path, { d: tallMouthPaths[state], fill: "none", stroke: "#1E293B", strokeWidth: 2, strokeLinecap: "round", animate: { d: tallMouthPaths[state] }, transition: transition, transform: "translate(0, -49)" })] })] }));
|
|
35
45
|
};
|
|
36
46
|
|
|
37
47
|
export { TallCharacter };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import React__default, {
|
|
3
|
-
import { useReducedMotion,
|
|
2
|
+
import React__default, { useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
|
|
3
|
+
import { useReducedMotion, motion, AnimatePresence } from 'motion/react';
|
|
4
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
5
|
|
|
6
|
+
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
6
7
|
/**
|
|
7
8
|
* MorphingText smoothly morphs between different text strings using a blur/fade
|
|
8
9
|
* transition. Two overlapping text layers cross-fade with a Gaussian blur effect
|
|
@@ -18,11 +19,12 @@ import { cn } from '../../../utils/cn.js';
|
|
|
18
19
|
* ```
|
|
19
20
|
*/
|
|
20
21
|
const MorphingText = React__default.forwardRef(({ texts, interval = 3000, transitionDuration = 800, loop = true, pauseOnHover = false, currentIndex: controlledIndex, onTextChange, className, ...props }, ref) => {
|
|
21
|
-
const filterId = useId();
|
|
22
22
|
const shouldReduceMotion = useReducedMotion();
|
|
23
23
|
const isControlled = controlledIndex !== undefined;
|
|
24
24
|
const [internalIndex, setInternalIndex] = useState(0);
|
|
25
25
|
const activeIndex = isControlled ? controlledIndex : internalIndex;
|
|
26
|
+
const measureRefs = useRef([]);
|
|
27
|
+
const [textRects, setTextRects] = useState([]);
|
|
26
28
|
const [isPaused, setIsPaused] = useState(false);
|
|
27
29
|
const intervalRef = useRef(null);
|
|
28
30
|
const hasReachedEnd = useRef(false);
|
|
@@ -59,6 +61,28 @@ const MorphingText = React__default.forwardRef(({ texts, interval = 3000, transi
|
|
|
59
61
|
useEffect(() => {
|
|
60
62
|
hasReachedEnd.current = false;
|
|
61
63
|
}, [loop, texts]);
|
|
64
|
+
useIsomorphicLayoutEffect(() => {
|
|
65
|
+
const measure = () => {
|
|
66
|
+
setTextRects(texts.map((_, index) => {
|
|
67
|
+
var _a, _b, _c;
|
|
68
|
+
const rect = (_a = measureRefs.current[index]) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
69
|
+
return {
|
|
70
|
+
width: (_b = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _b !== void 0 ? _b : 0,
|
|
71
|
+
height: (_c = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _c !== void 0 ? _c : 0,
|
|
72
|
+
};
|
|
73
|
+
}));
|
|
74
|
+
};
|
|
75
|
+
measure();
|
|
76
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const observer = new ResizeObserver(measure);
|
|
80
|
+
measureRefs.current.forEach((element) => {
|
|
81
|
+
if (element)
|
|
82
|
+
observer.observe(element);
|
|
83
|
+
});
|
|
84
|
+
return () => observer.disconnect();
|
|
85
|
+
}, [texts]);
|
|
62
86
|
// Fire onTextChange for controlled index changes
|
|
63
87
|
useEffect(() => {
|
|
64
88
|
if (isControlled) {
|
|
@@ -78,14 +102,23 @@ const MorphingText = React__default.forwardRef(({ texts, interval = 3000, transi
|
|
|
78
102
|
}
|
|
79
103
|
const durationSeconds = transitionDuration / 1000;
|
|
80
104
|
const currentText = texts[safeIndex];
|
|
105
|
+
const currentRect = textRects[safeIndex];
|
|
106
|
+
const measuredSize = (currentRect === null || currentRect === void 0 ? void 0 : currentRect.width) && (currentRect === null || currentRect === void 0 ? void 0 : currentRect.height)
|
|
107
|
+
? { width: currentRect.width, height: currentRect.height }
|
|
108
|
+
: undefined;
|
|
81
109
|
// Reduced motion: instant swap with no animation
|
|
82
110
|
if (shouldReduceMotion) {
|
|
83
111
|
return (jsx("div", { ref: ref, className: cn('relative inline-block', className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, "aria-live": "polite", "aria-atomic": "true", ...props, children: jsx("span", { children: currentText }) }));
|
|
84
112
|
}
|
|
85
|
-
return (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
113
|
+
return (jsx("div", { ref: ref, className: cn('relative inline-block align-bottom', className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, "aria-live": "polite", "aria-atomic": "true", ...props, children: jsxs(motion.span, { className: "relative inline-block align-bottom", initial: false, animate: measuredSize, transition: {
|
|
114
|
+
duration: durationSeconds,
|
|
115
|
+
ease: 'easeInOut',
|
|
116
|
+
}, children: [jsx("span", { className: "pointer-events-none invisible absolute left-0 top-0 whitespace-nowrap", "aria-hidden": "true", children: texts.map((text, index) => (jsx("span", { ref: (element) => {
|
|
117
|
+
measureRefs.current[index] = element;
|
|
118
|
+
}, className: "block w-fit whitespace-nowrap", children: text }, `${text}-${index}`))) }), !measuredSize && (jsx("span", { className: "whitespace-nowrap", children: currentText })), measuredSize && (jsx(AnimatePresence, { mode: "popLayout", initial: false, children: jsx(motion.span, { className: "absolute left-0 top-0 flex whitespace-nowrap", initial: { opacity: 0, filter: 'blur(8px)' }, animate: { opacity: 1, filter: 'blur(0px)' }, exit: { opacity: 0, filter: 'blur(8px)' }, transition: {
|
|
119
|
+
duration: durationSeconds,
|
|
120
|
+
ease: 'easeInOut',
|
|
121
|
+
}, children: currentText }, safeIndex) }))] }) }));
|
|
89
122
|
});
|
|
90
123
|
MorphingText.displayName = "MorphingText";
|
|
91
124
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { StripePatternProps } from "./StripePattern.types";
|
|
3
|
+
/**
|
|
4
|
+
* StripePattern renders an SVG-based diagonal stripe background pattern.
|
|
5
|
+
*
|
|
6
|
+
* Use it as an absolute-positioned decorative or placeholder fill inside a
|
|
7
|
+
* container with `position: relative`. The pattern is non-interactive
|
|
8
|
+
* (`pointer-events: none`) and sits at `z-index: 0` by default.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <div className="relative overflow-hidden h-40 rounded-lg">
|
|
13
|
+
* <StripePattern />
|
|
14
|
+
* <div className="relative z-10">Empty cover slot</div>
|
|
15
|
+
* </div>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const StripePattern: React.ForwardRefExoticComponent<StripePatternProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import React__default, { useId, useMemo } from 'react';
|
|
3
|
+
import { useReducedMotion, motion } from 'motion/react';
|
|
4
|
+
import { cn } from '../../../utils/cn.js';
|
|
5
|
+
|
|
6
|
+
function buildMaskImage(direction, fadeSize) {
|
|
7
|
+
const stop = Math.max(0, Math.min(100, fadeSize));
|
|
8
|
+
if (direction === "radial") {
|
|
9
|
+
return `radial-gradient(ellipse at center, black ${100 - stop}%, transparent 100%)`;
|
|
10
|
+
}
|
|
11
|
+
const directionMap = {
|
|
12
|
+
top: "to top",
|
|
13
|
+
bottom: "to bottom",
|
|
14
|
+
left: "to left",
|
|
15
|
+
right: "to right",
|
|
16
|
+
};
|
|
17
|
+
return `linear-gradient(${directionMap[direction]}, black ${100 - stop}%, transparent 100%)`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* StripePattern renders an SVG-based diagonal stripe background pattern.
|
|
21
|
+
*
|
|
22
|
+
* Use it as an absolute-positioned decorative or placeholder fill inside a
|
|
23
|
+
* container with `position: relative`. The pattern is non-interactive
|
|
24
|
+
* (`pointer-events: none`) and sits at `z-index: 0` by default.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <div className="relative overflow-hidden h-40 rounded-lg">
|
|
29
|
+
* <StripePattern />
|
|
30
|
+
* <div className="relative z-10">Empty cover slot</div>
|
|
31
|
+
* </div>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
const StripePattern = React__default.forwardRef(({ spacing = 12, strokeWidth = 6, angle = -45, color = "color-mix(in srgb, var(--color-border) 50%, transparent)", fade = false, fadeDirection = "radial", fadeSize = 50, animated = false, className, style, ...rest }, ref) => {
|
|
35
|
+
const patternId = useId();
|
|
36
|
+
const shouldReduceMotion = useReducedMotion();
|
|
37
|
+
const enableAnimation = animated && !shouldReduceMotion;
|
|
38
|
+
const maskImage = useMemo(() => (fade ? buildMaskImage(fadeDirection, fadeSize) : undefined), [fade, fadeDirection, fadeSize]);
|
|
39
|
+
const containerStyle = {
|
|
40
|
+
...style,
|
|
41
|
+
...(maskImage
|
|
42
|
+
? {
|
|
43
|
+
maskImage,
|
|
44
|
+
WebkitMaskImage: maskImage,
|
|
45
|
+
}
|
|
46
|
+
: {}),
|
|
47
|
+
};
|
|
48
|
+
const lineX = spacing / 2;
|
|
49
|
+
const lineElement = enableAnimation ? (jsx(motion.line, { x1: lineX, y1: 0, x2: lineX, y2: spacing, stroke: color, strokeWidth: strokeWidth, animate: { opacity: [0.4, 1, 0.4] }, transition: { duration: 4, ease: "easeInOut", repeat: Infinity } })) : (jsx("line", { x1: lineX, y1: 0, x2: lineX, y2: spacing, stroke: color, strokeWidth: strokeWidth }));
|
|
50
|
+
return (jsx("div", { ref: ref, className: cn("absolute inset-0 pointer-events-none z-0 overflow-hidden", className), style: containerStyle, ...rest, children: jsxs("svg", { "aria-hidden": "true", className: "absolute inset-0 w-full h-full", children: [jsx("defs", { children: jsx("pattern", { id: patternId, x: "0", y: "0", width: spacing, height: spacing, patternUnits: "userSpaceOnUse", patternTransform: `rotate(${angle})`, children: lineElement }) }), jsx("rect", { width: "100%", height: "100%", fill: `url(#${patternId})` })] }) }));
|
|
51
|
+
});
|
|
52
|
+
StripePattern.displayName = "StripePattern";
|
|
53
|
+
|
|
54
|
+
export { StripePattern };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/** Direction for the fade gradient mask */
|
|
3
|
+
export type StripePatternFadeDirection = "radial" | "top" | "bottom" | "left" | "right";
|
|
4
|
+
export interface StripePatternProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/**
|
|
6
|
+
* Distance between adjacent stripes in pixels (center to center).
|
|
7
|
+
* @default 12
|
|
8
|
+
*/
|
|
9
|
+
spacing?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Thickness of each stripe in pixels.
|
|
12
|
+
* @default 6
|
|
13
|
+
*/
|
|
14
|
+
strokeWidth?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Stripe rotation in degrees. `-45` runs bottom-left to top-right.
|
|
17
|
+
* @default -45
|
|
18
|
+
*/
|
|
19
|
+
angle?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Stroke color of the stripes.
|
|
22
|
+
* Accepts any valid CSS color value or CSS variable.
|
|
23
|
+
* @default "color-mix(in srgb, var(--color-border) 50%, transparent)"
|
|
24
|
+
*/
|
|
25
|
+
color?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Whether to apply a gradient mask that fades the pattern at the edges.
|
|
28
|
+
* Defaults to `false` so the pattern reads as a flat fill — useful for
|
|
29
|
+
* placeholder surfaces. Set to `true` for decorative backgrounds.
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
fade?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Direction of the fade gradient.
|
|
35
|
+
* - "radial" fades from center outward
|
|
36
|
+
* - "top" | "bottom" | "left" | "right" fades toward that edge
|
|
37
|
+
* @default "radial"
|
|
38
|
+
*/
|
|
39
|
+
fadeDirection?: StripePatternFadeDirection;
|
|
40
|
+
/**
|
|
41
|
+
* Size of the fade area as a percentage (0-100).
|
|
42
|
+
* @default 50
|
|
43
|
+
*/
|
|
44
|
+
fadeSize?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Whether to apply a subtle pulse animation to the stripes.
|
|
47
|
+
* Respects prefers-reduced-motion automatically.
|
|
48
|
+
* @default false
|
|
49
|
+
*/
|
|
50
|
+
animated?: boolean;
|
|
51
|
+
/** Additional className for style overrides (shadcn pattern). */
|
|
52
|
+
className?: string;
|
|
53
|
+
}
|
|
@@ -20,10 +20,14 @@ export { GridPattern } from "./GridPattern";
|
|
|
20
20
|
export type { GridPatternProps, GridPatternFadeDirection, } from "./GridPattern";
|
|
21
21
|
export { DotPattern } from "./DotPattern";
|
|
22
22
|
export type { DotPatternProps, DotPatternFadeDirection, } from "./DotPattern";
|
|
23
|
+
export { StripePattern } from "./StripePattern";
|
|
24
|
+
export type { StripePatternProps, StripePatternFadeDirection, } from "./StripePattern";
|
|
23
25
|
export { NoiseOverlay } from "./NoiseOverlay";
|
|
24
26
|
export type { NoiseOverlayProps, NoiseBlendMode } from "./NoiseOverlay";
|
|
25
27
|
export { Spotlight } from "./Spotlight";
|
|
26
28
|
export type { SpotlightProps } from "./Spotlight";
|
|
29
|
+
export { SpotlightBorder } from "./SpotlightBorder";
|
|
30
|
+
export type { SpotlightBorderProps, SpotlightBorderVariation, } from "./SpotlightBorder";
|
|
27
31
|
export { ParallaxSection } from "./ParallaxSection";
|
|
28
32
|
export type { ParallaxSectionProps } from "./ParallaxSection";
|
|
29
33
|
export { ScrollReveal } from "./ScrollReveal";
|
|
@@ -36,3 +40,8 @@ export { InteractiveCharacters } from "./InteractiveCharacters";
|
|
|
36
40
|
export type { InteractiveCharactersProps, CharacterState, CharacterSize, } from "./InteractiveCharacters";
|
|
37
41
|
export { InsetCircleButton, InsetPill } from "./Neumorphic";
|
|
38
42
|
export type { InsetCircleButtonProps, InsetPillProps, } from "./Neumorphic";
|
|
43
|
+
export { AIOrb } from "./AIOrb";
|
|
44
|
+
export type { AIOrbProps, OrbState } from "./AIOrb";
|
|
45
|
+
export type { DotSphereProps, LiquidProps, AuraProps } from "./AIOrb";
|
|
46
|
+
export { Particles } from "./Particles";
|
|
47
|
+
export type { ParticlesProps } from "./Particles";
|
|
@@ -9,8 +9,10 @@ export { MagneticElement } from './MagneticElement/MagneticElement.js';
|
|
|
9
9
|
export { MorphingText } from './MorphingText/MorphingText.js';
|
|
10
10
|
export { GridPattern } from './GridPattern/GridPattern.js';
|
|
11
11
|
export { DotPattern } from './DotPattern/DotPattern.js';
|
|
12
|
+
export { StripePattern } from './StripePattern/StripePattern.js';
|
|
12
13
|
export { NoiseOverlay } from './NoiseOverlay/NoiseOverlay.js';
|
|
13
14
|
export { Spotlight } from './Spotlight/Spotlight.js';
|
|
15
|
+
export { SpotlightBorder } from './SpotlightBorder/SpotlightBorder.js';
|
|
14
16
|
export { ParallaxSection } from './ParallaxSection/ParallaxSection.js';
|
|
15
17
|
export { ScrollReveal } from './ScrollReveal/ScrollReveal.js';
|
|
16
18
|
export { StickyScroll } from './StickyScroll/StickyScroll.js';
|
|
@@ -18,3 +20,5 @@ export { PageTransition } from './PageTransition/PageTransition.js';
|
|
|
18
20
|
export { InteractiveCharacters } from './InteractiveCharacters/InteractiveCharacters.js';
|
|
19
21
|
export { InsetCircleButton } from './Neumorphic/InsetCircleButton.js';
|
|
20
22
|
export { InsetPill } from './Neumorphic/InsetPill.js';
|
|
23
|
+
export { AIOrb } from './AIOrb/AIOrb.js';
|
|
24
|
+
export { Particles } from './Particles/Particles.js';
|
|
@@ -10,7 +10,7 @@ const checkboxTheme = {
|
|
|
10
10
|
// Default state - light
|
|
11
11
|
"border-[var(--color-border)] bg-[var(--color-surface)] " +
|
|
12
12
|
// Default state - dark
|
|
13
|
-
"dark:bg-[var(--color-
|
|
13
|
+
"dark:bg-[var(--color-surface-raised)] " +
|
|
14
14
|
// Checked state - light
|
|
15
15
|
"checked:border-[var(--color-primary)] checked:bg-[var(--color-primary)] " +
|
|
16
16
|
// Checked state - dark
|