@flikk/ui 1.0.0-beta.25 → 1.0.0-beta.27
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 +4 -0
- package/dist/components/ai/PromptInput/VoiceRecorder.js +4 -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/Breadcrumbs/Breadcrumbs.theme.js +1 -1
- package/dist/components/core/Button/Button.js +1 -1
- package/dist/components/core/Button/Button.theme.js +5 -5
- package/dist/components/core/Calendar/Calendar.theme.js +1 -1
- package/dist/components/core/Calendar/CalendarMini/CalendarMini.theme.js +1 -1
- package/dist/components/core/CommandPalette/CommandItem.js +4 -5
- package/dist/components/core/CommandPalette/CommandPalette.js +1 -1
- package/dist/components/core/CommandPalette/CommandPalette.theme.js +11 -13
- package/dist/components/core/CommandPalette/CommandPalette.types.d.ts +0 -2
- package/dist/components/core/ContextMenu/ContextMenu.theme.js +13 -14
- package/dist/components/core/DotSeparator/DotSeparator.d.ts +3 -0
- package/dist/components/core/DotSeparator/DotSeparator.js +19 -0
- package/dist/components/core/DotSeparator/DotSeparator.theme.d.ts +2 -0
- package/dist/components/core/DotSeparator/DotSeparator.theme.js +10 -0
- package/dist/components/core/DotSeparator/DotSeparator.types.d.ts +25 -0
- package/dist/components/core/DotSeparator/index.d.ts +3 -0
- package/dist/components/core/Drawer/Drawer.theme.js +2 -2
- package/dist/components/core/Dropdown/Dropdown.theme.js +4 -5
- package/dist/components/core/Dropdown/DropdownItem.js +1 -1
- package/dist/components/core/Dropdown/DropdownMenu.js +1 -1
- package/dist/components/core/Empty/Empty.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/MenuItem/MenuItem.js +5 -2
- package/dist/components/core/MenuItem/MenuItem.theme.d.ts +6 -0
- package/dist/components/core/MenuItem/MenuItem.theme.js +19 -16
- package/dist/components/core/MenuItem/MenuItem.types.d.ts +2 -0
- 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 +187 -68
- package/dist/components/core/NavItem/NavItem.theme.js +31 -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/Popover/Popover.types.d.ts +6 -2
- package/dist/components/core/Popover/PopoverBody.js +10 -3
- 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 +3 -1
- package/dist/components/core/Sidebar/Sidebar.js +53 -16
- package/dist/components/core/Sidebar/Sidebar.theme.js +5 -7
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +125 -8
- package/dist/components/core/Sidebar/SidebarCard.d.ts +25 -0
- package/dist/components/core/Sidebar/SidebarCard.js +68 -0
- package/dist/components/core/Sidebar/SidebarCard.theme.d.ts +8 -0
- package/dist/components/core/Sidebar/SidebarCard.theme.js +18 -0
- package/dist/components/core/Sidebar/SidebarContent.js +10 -6
- package/dist/components/core/Sidebar/SidebarContext.d.ts +17 -16
- package/dist/components/core/Sidebar/SidebarContext.js +43 -20
- package/dist/components/core/Sidebar/SidebarFooter.js +4 -6
- package/dist/components/core/Sidebar/SidebarHeader.js +12 -9
- package/dist/components/core/Sidebar/SidebarLogo.js +39 -8
- package/dist/components/core/Sidebar/SidebarNav.js +4 -3
- package/dist/components/core/Sidebar/SidebarNavGroup.js +4 -7
- package/dist/components/core/Sidebar/SidebarSearch.d.ts +22 -0
- package/dist/components/core/Sidebar/SidebarSearch.js +135 -0
- 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 +4 -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.js +29 -5
- package/dist/components/core/Tag/Tag.theme.js +14 -9
- package/dist/components/core/Tag/Tag.types.d.ts +7 -0
- package/dist/components/core/Tooltip/Tooltip.js +1 -1
- package/dist/components/core/Tooltip/Tooltip.theme.js +1 -2
- package/dist/components/core/index.d.ts +2 -0
- package/dist/components/core/index.js +6 -2
- package/dist/components/data-display/GanttChart/GanttChart.d.ts +1 -1
- package/dist/components/data-display/GanttChart/GanttChart.js +31 -4
- package/dist/components/data-display/GanttChart/GanttChart.theme.js +19 -17
- package/dist/components/data-display/GanttChart/GanttChart.types.d.ts +19 -0
- package/dist/components/data-display/GanttChart/GanttChart.utils.js +1 -1
- package/dist/components/data-display/GanttChart/GanttChartPanel.d.ts +1 -1
- package/dist/components/data-display/GanttChart/GanttChartPanel.js +3 -3
- package/dist/components/data-display/GanttChart/GanttDependencyArrows.js +7 -4
- package/dist/components/data-display/GanttChart/GanttMilestone.js +5 -5
- package/dist/components/data-display/GanttChart/GanttTableGroupRow.js +1 -1
- package/dist/components/data-display/GanttChart/GanttTablePanel.js +1 -1
- package/dist/components/data-display/GanttChart/GanttTableTaskRow.js +2 -2
- package/dist/components/data-display/GanttChart/GanttTaskBar.js +37 -16
- package/dist/components/data-display/GanttChart/GanttTimelineGrid.js +2 -1
- package/dist/components/data-display/GanttChart/GanttTimelineHeader.js +35 -11
- package/dist/components/data-display/GanttChart/GanttToolbar.js +2 -2
- package/dist/components/data-display/GanttChart/hooks/useGanttDrag.js +3 -2
- package/dist/components/data-display/GanttChart/index.d.ts +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 +14 -6
- 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/effects/Aurora/Aurora.d.ts +6 -17
- package/dist/components/effects/Aurora/Aurora.js +310 -105
- package/dist/components/effects/Aurora/Aurora.types.d.ts +6 -0
- 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 +2 -0
- package/dist/components/effects/index.js +1 -0
- package/dist/components/forms/Checkbox/Checkbox.theme.js +1 -1
- package/dist/components/forms/Combobox/Combobox.js +1 -1
- package/dist/components/forms/Combobox/Combobox.theme.js +2 -4
- package/dist/components/forms/CronInput/CronInput.js +1 -1
- package/dist/components/forms/CronInput/CronInput.theme.js +22 -31
- package/dist/components/forms/DatePicker/DatePicker.theme.js +11 -18
- package/dist/components/forms/DatePicker/DatePickerTrigger.js +1 -1
- package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +7 -14
- package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +1 -1
- package/dist/components/forms/FileUpload/FileUpload.animations.d.ts +29 -0
- package/dist/components/forms/FileUpload/FileUpload.animations.js +19 -0
- package/dist/components/forms/FileUpload/FileUpload.js +88 -49
- package/dist/components/forms/FileUpload/FileUpload.theme.d.ts +2 -10
- package/dist/components/forms/FileUpload/FileUpload.theme.js +16 -30
- package/dist/components/forms/FileUpload/FileUpload.types.d.ts +26 -10
- 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 +16 -15
- package/dist/components/forms/InputCounter/InputCounter.theme.js +1 -1
- package/dist/components/forms/Mention/Mention.js +152 -57
- package/dist/components/forms/Mention/Mention.theme.js +16 -23
- package/dist/components/forms/Mention/Mention.types.d.ts +12 -12
- package/dist/components/forms/Mention/Mention.utils.js +32 -4
- 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/Select/Select.theme.js +3 -3
- 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 +4 -0
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
- package/dist/components/forms/forms.theme.js +24 -19
- package/dist/components/forms/index.js +4 -0
- package/dist/components/layout/AppShell/AppShell.d.ts +10 -0
- package/dist/components/layout/AppShell/AppShell.js +84 -0
- package/dist/components/layout/AppShell/AppShell.theme.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShell.theme.js +42 -0
- package/dist/components/layout/AppShell/AppShell.types.d.ts +127 -0
- package/dist/components/layout/AppShell/AppShellContext.d.ts +14 -0
- package/dist/components/layout/AppShell/AppShellContext.js +19 -0
- package/dist/components/layout/AppShell/AppShellFooter.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellFooter.js +12 -0
- package/dist/components/layout/AppShell/AppShellHeader.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellHeader.js +12 -0
- package/dist/components/layout/AppShell/AppShellMain.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellMain.js +19 -0
- package/dist/components/layout/AppShell/AppShellRightPanel.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellRightPanel.js +16 -0
- package/dist/components/layout/AppShell/AppShellSidebar.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellSidebar.js +16 -0
- package/dist/components/layout/AppShell/AppShellTopbar.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellTopbar.js +12 -0
- package/dist/components/layout/AppShell/index.d.ts +3 -0
- package/dist/components/layout/FormLayout/FormLayout.js +9 -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/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.js +2 -0
- package/dist/hooks/useSelectPortal.d.ts +10 -2
- package/dist/hooks/useSelectPortal.js +45 -24
- package/dist/index.js +8 -2
- package/dist/registry.json +10021 -0
- package/dist/styles.css +1 -1
- package/dist/utils/dateUtils.js +11 -1
- package/package.json +4 -2
- package/src/global.scss +29 -8
- package/src/styles/theme.css +174 -100
- package/dist/components/core/Sidebar/SidebarSubmenu.d.ts +0 -7
- package/dist/components/core/Sidebar/SidebarSubmenu.js +0 -12
|
@@ -1,119 +1,324 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { useRef, useState, useEffect } from 'react';
|
|
3
|
+
import { useReducedMotion } from 'motion/react';
|
|
3
4
|
import { cn } from '../../../utils/cn.js';
|
|
4
5
|
|
|
5
|
-
const DEFAULT_COLORS = ['#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const blobAnimations = [
|
|
12
|
-
{
|
|
13
|
-
// Blob 0 -- slow horizontal drift with vertical bob
|
|
14
|
-
x: ['0%', '30%', '-20%', '10%', '0%'],
|
|
15
|
-
y: ['0%', '-25%', '15%', '-10%', '0%'],
|
|
16
|
-
scale: [1, 1.15, 0.9, 1.05, 1],
|
|
17
|
-
baseDuration: 20,
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
// Blob 1 -- figure-8-ish path, slightly faster
|
|
21
|
-
x: ['0%', '-35%', '25%', '-15%', '0%'],
|
|
22
|
-
y: ['0%', '20%', '-30%', '25%', '0%'],
|
|
23
|
-
scale: [1, 0.9, 1.2, 0.95, 1],
|
|
24
|
-
baseDuration: 23,
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
// Blob 2 -- wide circular sweep
|
|
28
|
-
x: ['0%', '25%', '0%', '-25%', '0%'],
|
|
29
|
-
y: ['0%', '-20%', '25%', '-15%', '0%'],
|
|
30
|
-
scale: [1, 1.1, 0.85, 1.15, 1],
|
|
31
|
-
baseDuration: 27,
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
// Blob 3 -- gentle diagonal drift
|
|
35
|
-
x: ['0%', '-20%', '30%', '-10%', '0%'],
|
|
36
|
-
y: ['0%', '30%', '-20%', '10%', '0%'],
|
|
37
|
-
scale: [1, 0.95, 1.1, 0.9, 1],
|
|
38
|
-
baseDuration: 31,
|
|
39
|
-
},
|
|
40
|
-
];
|
|
6
|
+
const DEFAULT_COLORS = ['#080f1a', '#0b2545', '#13d378', '#00f5ff'];
|
|
7
|
+
const clamp = (value, min, max) => {
|
|
8
|
+
if (Number.isNaN(value))
|
|
9
|
+
return min;
|
|
10
|
+
return Math.min(Math.max(value, min), max);
|
|
11
|
+
};
|
|
41
12
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
13
|
+
* Parser to safely convert hex, rgb/rgba, or standard keyword color strings
|
|
14
|
+
* to normalized floating-point RGB arrays ready for GPU shader uniforms.
|
|
44
15
|
*/
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
];
|
|
16
|
+
const parseColorToRGB = (colorStr) => {
|
|
17
|
+
if (colorStr.startsWith('#')) {
|
|
18
|
+
const hex = colorStr.substring(1);
|
|
19
|
+
if (hex.length === 3) {
|
|
20
|
+
const r = parseInt(hex[0] + hex[0], 16) / 255;
|
|
21
|
+
const g = parseInt(hex[1] + hex[1], 16) / 255;
|
|
22
|
+
const b = parseInt(hex[2] + hex[2], 16) / 255;
|
|
23
|
+
return [r, g, b];
|
|
24
|
+
}
|
|
25
|
+
else if (hex.length === 6) {
|
|
26
|
+
const r = parseInt(hex.substring(0, 2), 16) / 255;
|
|
27
|
+
const g = parseInt(hex.substring(2, 4), 16) / 255;
|
|
28
|
+
const b = parseInt(hex.substring(4, 6), 16) / 255;
|
|
29
|
+
return [r, g, b];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const rgbMatch = colorStr.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
|
|
33
|
+
if (rgbMatch) {
|
|
34
|
+
return [
|
|
35
|
+
parseInt(rgbMatch[1], 10) / 255,
|
|
36
|
+
parseInt(rgbMatch[2], 10) / 255,
|
|
37
|
+
parseInt(rgbMatch[3], 10) / 255,
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
const colorsMap = {
|
|
41
|
+
red: [1, 0, 0],
|
|
42
|
+
green: [0, 1, 0],
|
|
43
|
+
blue: [0, 0, 1],
|
|
44
|
+
yellow: [1, 1, 0],
|
|
45
|
+
cyan: [0, 1, 1],
|
|
46
|
+
magenta: [1, 0, 1],
|
|
47
|
+
white: [1, 1, 1],
|
|
48
|
+
black: [0, 0, 0],
|
|
49
|
+
transparent: [0, 0, 0],
|
|
50
|
+
};
|
|
51
|
+
return colorsMap[colorStr.toLowerCase()] || [0.23, 0.51, 0.96]; // hex #3b82f6 equivalent fallback
|
|
52
|
+
};
|
|
53
|
+
// --- WebGL Shader Sources ---
|
|
54
|
+
const VERTEX_SHADER_SOURCE = `
|
|
55
|
+
attribute vec2 position;
|
|
56
|
+
void main() {
|
|
57
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
const FRAGMENT_SHADER_SOURCE = `
|
|
61
|
+
precision mediump float;
|
|
62
|
+
uniform vec2 u_resolution;
|
|
63
|
+
uniform float u_time;
|
|
64
|
+
uniform vec3 u_color1; // Base ambient background (e.g. dark blue)
|
|
65
|
+
uniform vec3 u_color2; // Mid transition (e.g. indigo/teal)
|
|
66
|
+
uniform vec3 u_color3; // Curtain primary ray color (e.g. bright green)
|
|
67
|
+
uniform vec3 u_color4; // Curtain secondary highlight (e.g. cyan/emerald)
|
|
68
|
+
uniform float u_opacity;
|
|
69
|
+
uniform float u_speed;
|
|
70
|
+
uniform float u_size;
|
|
71
|
+
uniform float u_noise_opacity;
|
|
72
|
+
|
|
73
|
+
// Pseudo-random noise
|
|
74
|
+
float rand(vec2 co) {
|
|
75
|
+
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 2D Noise
|
|
79
|
+
float noise(vec2 st) {
|
|
80
|
+
vec2 i = floor(st);
|
|
81
|
+
vec2 f = fract(st);
|
|
82
|
+
float a = rand(i);
|
|
83
|
+
float b = rand(i + vec2(1.0, 0.0));
|
|
84
|
+
float c = rand(i + vec2(0.0, 1.0));
|
|
85
|
+
float d = rand(i + vec2(1.0, 1.0));
|
|
86
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
87
|
+
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Fractional Brownian Motion for slow horizontal waving drift
|
|
91
|
+
#define OCTAVES 3
|
|
92
|
+
float fbm(vec2 st) {
|
|
93
|
+
float value = 0.0;
|
|
94
|
+
float amplitude = 0.5;
|
|
95
|
+
for (int i = 0; i < OCTAVES; i++) {
|
|
96
|
+
value += amplitude * noise(st);
|
|
97
|
+
st *= 2.0;
|
|
98
|
+
amplitude *= 0.5;
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void main() {
|
|
104
|
+
// Normalize coordinates
|
|
105
|
+
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
|
|
106
|
+
|
|
107
|
+
// Scale speed factors
|
|
108
|
+
float t1 = u_time * 0.16 * u_speed;
|
|
109
|
+
float t2 = u_time * -0.09 * u_speed;
|
|
110
|
+
|
|
111
|
+
// 1. Warp X coordinate slowly to create organic curving curtain ribbons
|
|
112
|
+
vec2 warpUV = vec2(uv.x * 1.5, uv.y * 0.5) + t1 * 0.1;
|
|
113
|
+
float warp = fbm(warpUV);
|
|
114
|
+
|
|
115
|
+
// Highly stretch the X coordinate (e.g. 9.5) and compress Y coordinate to form vertical rays
|
|
116
|
+
float ray1_x = (uv.x * 9.5 / u_size) + warp * 2.2 - t1;
|
|
117
|
+
float ray2_x = (uv.x * 6.8 / u_size) - warp * 1.8 + t2;
|
|
118
|
+
|
|
119
|
+
// 2. Sample multiple curtain layers
|
|
120
|
+
float ray1 = noise(vec2(ray1_x, uv.y * 0.3));
|
|
121
|
+
ray1 = pow(ray1, 3.2); // Elevate to power to sharpen vertical columns/comb rays
|
|
122
|
+
|
|
123
|
+
float ray2 = noise(vec2(ray2_x, uv.y * 0.25));
|
|
124
|
+
ray2 = pow(ray2, 2.5);
|
|
125
|
+
|
|
126
|
+
// 3. Ambient sky glow at the base and transition layers
|
|
127
|
+
float baseGlow = smoothstep(0.0, 0.5, uv.y) * smoothstep(1.0, 0.35, uv.y);
|
|
128
|
+
vec3 baseSky = mix(u_color1 * 0.15, u_color2 * 0.35, uv.y) * baseGlow;
|
|
129
|
+
|
|
130
|
+
// 4. Glowing curtains fading gracefully at bottom (above horizon) and top sky
|
|
131
|
+
float curtainMask = smoothstep(0.08, 0.38, uv.y) * smoothstep(0.95, 0.55, uv.y);
|
|
132
|
+
|
|
133
|
+
// Combine primary and secondary glowing curtains (super saturated)
|
|
134
|
+
vec3 curtains = (u_color3 * ray1 * 2.8) + (u_color4 * ray2 * 1.8);
|
|
135
|
+
|
|
136
|
+
// 5. Final color synthesis (combine ambient sky with neon curtains)
|
|
137
|
+
vec3 finalColor = baseSky + (curtains * curtainMask);
|
|
138
|
+
|
|
139
|
+
// 6. GPU stardust cinematic noise
|
|
140
|
+
float grain = rand(gl_FragCoord.xy + u_time) * 0.055;
|
|
141
|
+
finalColor += vec3(grain) * u_noise_opacity;
|
|
142
|
+
|
|
143
|
+
// Output with opacity controls
|
|
144
|
+
gl_FragColor = vec4(finalColor * u_opacity, u_opacity);
|
|
145
|
+
}
|
|
146
|
+
`;
|
|
51
147
|
/**
|
|
52
|
-
* Aurora creates
|
|
53
|
-
*
|
|
54
|
-
* Renders several large, blurred, semi-transparent blobs that slowly drift
|
|
55
|
-
* and morph, producing an organic aurora-like glow. Ideal for hero sections
|
|
56
|
-
* and decorative backgrounds.
|
|
148
|
+
* Aurora creates a highly vibrant, WebGL-powered multi-color gradient background effect.
|
|
57
149
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
150
|
+
* It uses a hardware-accelerated domain-warping Simplex Noise shader executing 100% on
|
|
151
|
+
* the GPU, achieving absolute 60/120 FPS butter-smooth rendering on all high-DPI screens.
|
|
152
|
+
* Features full support for custom blend modes and built-in stardust texture.
|
|
61
153
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* <div className="relative overflow-hidden h-96 bg-gray-950">
|
|
65
|
-
* <Aurora />
|
|
66
|
-
* <div className="relative z-10 text-white p-8">
|
|
67
|
-
* <h1>Hero Content</h1>
|
|
68
|
-
* </div>
|
|
69
|
-
* </div>
|
|
70
|
-
* ```
|
|
154
|
+
* If WebGL is unavailable or reduced motion is preferred, the component gracefully falls back
|
|
155
|
+
* to a gorgeous static CSS gradient layout to preserve SEO, styling, and accessibility.
|
|
71
156
|
*/
|
|
72
|
-
const Aurora = ({ colors = DEFAULT_COLORS, blur = 80, speed = 1, opacity = 0.3, size = 1, className, style, ...props }) => {
|
|
157
|
+
const Aurora = ({ colors = DEFAULT_COLORS, blur = 80, speed = 1, opacity = 0.3, size = 1, blendMode = 'screen', showNoise = true, noiseOpacity = 0.06, className, style, ...props }) => {
|
|
158
|
+
const canvasRef = useRef(null);
|
|
159
|
+
const containerRef = useRef(null);
|
|
160
|
+
const [webglSupported, setWebglSupported] = useState(true);
|
|
73
161
|
const shouldReduceMotion = useReducedMotion();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
162
|
+
const safeColors = colors.length > 0 ? colors : DEFAULT_COLORS;
|
|
163
|
+
const safeSpeed = Math.max(0.1, speed);
|
|
164
|
+
const safeOpacity = clamp(opacity, 0, 1);
|
|
165
|
+
const safeSize = Math.max(0.1, size);
|
|
166
|
+
// Fallback Ambient CSS wash
|
|
167
|
+
const wash = safeColors
|
|
168
|
+
.slice(0, 4)
|
|
169
|
+
.map((color, index) => {
|
|
170
|
+
const stops = ['20% 30%', '80% 20%', '30% 80%', '75% 75%'];
|
|
171
|
+
const isHex = color.startsWith('#') && (color.length === 7 || color.length === 4);
|
|
172
|
+
const opacityColor = isHex ? `${color}33` : color;
|
|
173
|
+
return `radial-gradient(circle at ${stops[index]}, ${opacityColor}, transparent 45%)`;
|
|
174
|
+
})
|
|
175
|
+
.join(', ');
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
// If reduced motion is preferred, bypass WebGL completely and rely on static CSS fallback
|
|
178
|
+
if (shouldReduceMotion) {
|
|
179
|
+
setWebglSupported(false);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const canvas = canvasRef.current;
|
|
183
|
+
if (!canvas)
|
|
184
|
+
return;
|
|
185
|
+
const gl = canvas.getContext('webgl', { antialias: false, alpha: true }) ||
|
|
186
|
+
canvas.getContext('experimental-webgl', { antialias: false, alpha: true });
|
|
187
|
+
if (!gl) {
|
|
188
|
+
console.warn('WebGL is not supported in this browser. Falling back to static CSS.');
|
|
189
|
+
setWebglSupported(false);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// Helper functions for shader compilation
|
|
193
|
+
const compileShader = (source, type) => {
|
|
194
|
+
const shader = gl.createShader(type);
|
|
195
|
+
if (!shader)
|
|
196
|
+
return null;
|
|
197
|
+
gl.shaderSource(shader, source);
|
|
198
|
+
gl.compileShader(shader);
|
|
199
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
200
|
+
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
|
|
201
|
+
gl.deleteShader(shader);
|
|
202
|
+
return null;
|
|
96
203
|
}
|
|
97
|
-
return
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
204
|
+
return shader;
|
|
205
|
+
};
|
|
206
|
+
const createProgram = (vsSource, fsSource) => {
|
|
207
|
+
const vs = compileShader(vsSource, gl.VERTEX_SHADER);
|
|
208
|
+
const fs = compileShader(fsSource, gl.FRAGMENT_SHADER);
|
|
209
|
+
if (!vs || !fs)
|
|
210
|
+
return null;
|
|
211
|
+
const prog = gl.createProgram();
|
|
212
|
+
if (!prog)
|
|
213
|
+
return null;
|
|
214
|
+
gl.attachShader(prog, vs);
|
|
215
|
+
gl.attachShader(prog, fs);
|
|
216
|
+
gl.linkProgram(prog);
|
|
217
|
+
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
|
218
|
+
console.error('Program link error:', gl.getProgramInfoLog(prog));
|
|
219
|
+
gl.deleteProgram(prog);
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
return prog;
|
|
223
|
+
};
|
|
224
|
+
const program = createProgram(VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);
|
|
225
|
+
if (!program) {
|
|
226
|
+
setWebglSupported(false);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// Buffer coordinates setup
|
|
230
|
+
const vertices = new Float32Array([
|
|
231
|
+
-1, -1,
|
|
232
|
+
1.0, -1,
|
|
233
|
+
-1, 1.0,
|
|
234
|
+
-1, 1.0,
|
|
235
|
+
1.0, -1,
|
|
236
|
+
1.0, 1.0,
|
|
237
|
+
]);
|
|
238
|
+
const positionBuffer = gl.createBuffer();
|
|
239
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
240
|
+
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
|
241
|
+
// Dynamic uniforms resolution mapping
|
|
242
|
+
const uResolution = gl.getUniformLocation(program, 'u_resolution');
|
|
243
|
+
const uTime = gl.getUniformLocation(program, 'u_time');
|
|
244
|
+
const uColor1 = gl.getUniformLocation(program, 'u_color1');
|
|
245
|
+
const uColor2 = gl.getUniformLocation(program, 'u_color2');
|
|
246
|
+
const uColor3 = gl.getUniformLocation(program, 'u_color3');
|
|
247
|
+
const uColor4 = gl.getUniformLocation(program, 'u_color4');
|
|
248
|
+
const uOpacity = gl.getUniformLocation(program, 'u_opacity');
|
|
249
|
+
const uSpeed = gl.getUniformLocation(program, 'u_speed');
|
|
250
|
+
const uSize = gl.getUniformLocation(program, 'u_size');
|
|
251
|
+
const uNoiseOpacity = gl.getUniformLocation(program, 'u_noise_opacity');
|
|
252
|
+
// Parse and cache floating RGB values
|
|
253
|
+
const rgbList = safeColors.slice(0, 4).map(parseColorToRGB);
|
|
254
|
+
while (rgbList.length < 4) {
|
|
255
|
+
rgbList.push(rgbList[rgbList.length - 1] || [0.23, 0.51, 0.96]);
|
|
256
|
+
}
|
|
257
|
+
let animationFrameId = 0;
|
|
258
|
+
let startTime = performance.now();
|
|
259
|
+
const resizeCanvas = () => {
|
|
260
|
+
const container = containerRef.current;
|
|
261
|
+
if (!container || !canvas)
|
|
262
|
+
return;
|
|
263
|
+
const width = container.clientWidth;
|
|
264
|
+
const height = container.clientHeight;
|
|
265
|
+
// Downsample DPR slightly on Retina screens (e.g. max 1.5 dpr) to optimize shader performance
|
|
266
|
+
const dpr = Math.min(window.devicePixelRatio || 1, 1.5);
|
|
267
|
+
canvas.width = Math.floor(width * dpr);
|
|
268
|
+
canvas.height = Math.floor(height * dpr);
|
|
269
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
270
|
+
};
|
|
271
|
+
resizeCanvas();
|
|
272
|
+
window.addEventListener('resize', resizeCanvas);
|
|
273
|
+
// Main Draw loop
|
|
274
|
+
const render = () => {
|
|
275
|
+
if (!canvas || !gl)
|
|
276
|
+
return;
|
|
277
|
+
const currentTime = (performance.now() - startTime) * 0.001;
|
|
278
|
+
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
|
279
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
280
|
+
gl.useProgram(program);
|
|
281
|
+
// Vertex configuration
|
|
282
|
+
const positionAttr = gl.getAttribLocation(program, 'position');
|
|
283
|
+
gl.enableVertexAttribArray(positionAttr);
|
|
284
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
285
|
+
gl.vertexAttribPointer(positionAttr, 2, gl.FLOAT, false, 0, 0);
|
|
286
|
+
// Bind dynamic uniform variables
|
|
287
|
+
gl.uniform2f(uResolution, canvas.width, canvas.height);
|
|
288
|
+
gl.uniform1f(uTime, currentTime);
|
|
289
|
+
gl.uniform1f(uOpacity, safeOpacity);
|
|
290
|
+
gl.uniform1f(uSpeed, safeSpeed);
|
|
291
|
+
gl.uniform1f(uSize, safeSize);
|
|
292
|
+
gl.uniform1f(uNoiseOpacity, showNoise ? noiseOpacity : 0.0);
|
|
293
|
+
gl.uniform3fv(uColor1, rgbList[0]);
|
|
294
|
+
gl.uniform3fv(uColor2, rgbList[1]);
|
|
295
|
+
gl.uniform3fv(uColor3, rgbList[2]);
|
|
296
|
+
gl.uniform3fv(uColor4, rgbList[3]);
|
|
297
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
298
|
+
animationFrameId = requestAnimationFrame(render);
|
|
299
|
+
};
|
|
300
|
+
render();
|
|
301
|
+
// Clean up WebGL assets on unmount
|
|
302
|
+
return () => {
|
|
303
|
+
cancelAnimationFrame(animationFrameId);
|
|
304
|
+
window.removeEventListener('resize', resizeCanvas);
|
|
305
|
+
gl.deleteBuffer(positionBuffer);
|
|
306
|
+
gl.deleteProgram(program);
|
|
307
|
+
};
|
|
308
|
+
}, [safeColors, safeSpeed, safeOpacity, safeSize, showNoise, noiseOpacity, shouldReduceMotion]);
|
|
309
|
+
return (jsx("div", { ref: containerRef, className: cn('absolute inset-0 overflow-hidden pointer-events-none select-none', className), style: {
|
|
310
|
+
...style,
|
|
311
|
+
}, "aria-hidden": "true", ...props, children: webglSupported ? (jsx("canvas", { ref: canvasRef, className: "absolute inset-0 w-full h-full", style: {
|
|
312
|
+
mixBlendMode: blendMode,
|
|
313
|
+
opacity: 1.0,
|
|
314
|
+
} })) : (
|
|
315
|
+
/* Dynamic static-CSS fallback mesh wash if WebGL is unavailable or reduced-motion is active */
|
|
316
|
+
jsx("div", { className: "absolute inset-0", style: {
|
|
317
|
+
background: wash,
|
|
318
|
+
opacity: safeOpacity * 0.65,
|
|
319
|
+
mixBlendMode: blendMode,
|
|
320
|
+
filter: `blur(${blur}px)`,
|
|
321
|
+
} })) }));
|
|
117
322
|
};
|
|
118
323
|
|
|
119
324
|
export { Aurora };
|
|
@@ -10,6 +10,12 @@ export interface AuroraProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
10
10
|
opacity?: number;
|
|
11
11
|
/** Blob size multiplier. Higher values = larger blobs (default: 1) */
|
|
12
12
|
size?: number;
|
|
13
|
+
/** CSS mix-blend-mode for the aurora blobs (default: 'screen') */
|
|
14
|
+
blendMode?: 'screen' | 'color-dodge' | 'overlay' | 'hard-light' | 'normal' | 'plus-lighter';
|
|
15
|
+
/** Whether to render a premium stardust film-grain noise overlay on top (default: true) */
|
|
16
|
+
showNoise?: boolean;
|
|
17
|
+
/** Opacity of the noise overlay, 0 to 1 (default: 0.06) */
|
|
18
|
+
noiseOpacity?: number;
|
|
13
19
|
/** Additional className for the container */
|
|
14
20
|
className?: string;
|
|
15
21
|
}
|
|
@@ -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 };
|