@flikk/ui 1.0.0-beta.26 → 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/PromptInput/PromptInput.js +3 -0
- package/dist/components/ai/PromptInput/VoiceRecorder.js +3 -0
- package/dist/components/core/Button/Button.theme.js +1 -1
- package/dist/components/core/Calendar/Calendar.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/Dropdown/Dropdown.theme.js +2 -3
- package/dist/components/core/Dropdown/DropdownItem.js +1 -1
- package/dist/components/core/Dropdown/DropdownMenu.js +1 -1
- 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/NavItem/NavItem.js +20 -4
- package/dist/components/core/NavItem/NavItem.theme.js +13 -2
- package/dist/components/core/Popover/Popover.types.d.ts +6 -2
- package/dist/components/core/Popover/PopoverBody.js +10 -3
- package/dist/components/core/Segmented/Segmented.theme.js +1 -1
- package/dist/components/core/Sidebar/Sidebar.d.ts +2 -0
- package/dist/components/core/Sidebar/Sidebar.js +15 -7
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +92 -4
- 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 +5 -1
- package/dist/components/core/Sidebar/SidebarContext.js +1 -1
- package/dist/components/core/Sidebar/SidebarHeader.js +10 -2
- package/dist/components/core/Sidebar/SidebarLogo.js +38 -3
- 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 +1 -1
- package/dist/components/core/Sidebar/SidebarUserProfile.js +2 -2
- package/dist/components/core/Sidebar/index.d.ts +3 -1
- package/dist/components/core/Tag/Tag.js +29 -5
- package/dist/components/core/Tag/Tag.theme.js +13 -8
- 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 +1 -0
- package/dist/components/core/index.js +4 -0
- 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/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/Table/Table.theme.js +3 -5
- package/dist/components/effects/Aurora/Aurora.d.ts +6 -17
- package/dist/components/effects/Aurora/Aurora.js +297 -131
- package/dist/components/effects/Aurora/Aurora.types.d.ts +6 -0
- 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 +14 -23
- 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 +6 -13
- 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 +87 -48
- 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/Input/Input.theme.js +13 -10
- 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/RichTextEditor/RichTextEditor.js +3 -3
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +5 -5
- package/dist/components/forms/Select/Select.js +2 -2
- package/dist/components/forms/Select/Select.theme.js +3 -3
- package/dist/components/forms/TimePicker/TimePickerContent.js +3 -0
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
- package/dist/components/forms/forms.theme.js +21 -16
- package/dist/components/forms/index.js +3 -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 +3 -0
- 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 +6 -0
- 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 +51 -12
|
@@ -2,38 +2,62 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { cn } from '../../../utils/cn.js';
|
|
3
3
|
import { useGanttContext } from './GanttChart.js';
|
|
4
4
|
import { formatDayLabel } from './GanttChart.utils.js';
|
|
5
|
-
import { isSameDay, isWeekend } from '../../../utils/dateUtils.js';
|
|
5
|
+
import { isSameDay, isWeekend, getWeekNumber } from '../../../utils/dateUtils.js';
|
|
6
6
|
|
|
7
7
|
function getDayLabel(day, zoom) {
|
|
8
8
|
switch (zoom) {
|
|
9
9
|
case "day":
|
|
10
10
|
return formatDayLabel(day, zoom);
|
|
11
11
|
case "week":
|
|
12
|
-
|
|
13
|
-
return day.getDay() === 1 ? `${day.getDate()}` : "";
|
|
12
|
+
return day.getDay() === 1 ? `W${getWeekNumber(day)}` : "";
|
|
14
13
|
case "quarter":
|
|
15
|
-
// Show day number on 1st, 8th, 15th, 22nd
|
|
16
14
|
return [1, 8, 15, 22].includes(day.getDate())
|
|
17
15
|
? `${day.getDate()}`
|
|
18
16
|
: "";
|
|
19
17
|
case "month":
|
|
20
|
-
// Show day number on 1st and 15th
|
|
21
|
-
return day.getDate() === 1 || day.getDate() === 15
|
|
22
|
-
? `${day.getDate()}`
|
|
23
|
-
: "";
|
|
24
|
-
case "year":
|
|
25
|
-
// Show only on 1st of each month
|
|
26
18
|
return day.getDate() === 1
|
|
27
19
|
? day.toLocaleDateString("en-US", { month: "short" })
|
|
28
20
|
: "";
|
|
21
|
+
case "year":
|
|
22
|
+
return day.getDate() === 1 && day.getMonth() === 0
|
|
23
|
+
? `${day.getFullYear()}`
|
|
24
|
+
: "";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function getHeaderSpans(days, monthSpans, zoom) {
|
|
28
|
+
if (zoom !== "month" && zoom !== "year") {
|
|
29
|
+
return monthSpans;
|
|
30
|
+
}
|
|
31
|
+
const spans = [];
|
|
32
|
+
let currentLabel = "";
|
|
33
|
+
let startIndex = 0;
|
|
34
|
+
days.forEach((day, index) => {
|
|
35
|
+
const label = zoom === "year"
|
|
36
|
+
? `${day.getFullYear()}`
|
|
37
|
+
: day.toLocaleDateString("en-US", { year: "numeric" });
|
|
38
|
+
if (index === 0) {
|
|
39
|
+
currentLabel = label;
|
|
40
|
+
startIndex = 0;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (label !== currentLabel) {
|
|
44
|
+
spans.push({ label: currentLabel, startIndex, span: index - startIndex });
|
|
45
|
+
currentLabel = label;
|
|
46
|
+
startIndex = index;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (days.length > 0) {
|
|
50
|
+
spans.push({ label: currentLabel, startIndex, span: days.length - startIndex });
|
|
29
51
|
}
|
|
52
|
+
return spans;
|
|
30
53
|
}
|
|
31
54
|
const GanttTimelineHeader = ({ days, monthSpans, }) => {
|
|
32
55
|
const { theme, dayWidth, zoom } = useGanttContext();
|
|
33
56
|
const totalWidth = days.length * dayWidth;
|
|
57
|
+
const headerSpans = getHeaderSpans(days, monthSpans, zoom);
|
|
34
58
|
const today = new Date();
|
|
35
59
|
today.setHours(0, 0, 0, 0);
|
|
36
|
-
return (jsxs("div", { className: cn(theme.timelineHeaderStyle), style: { width: totalWidth }, children: [jsx("div", { className: "flex", style: { height: 28 }, children:
|
|
60
|
+
return (jsxs("div", { className: cn(theme.timelineHeaderStyle), style: { width: totalWidth }, children: [jsx("div", { className: "flex", style: { height: 28 }, children: headerSpans.map((span) => (jsx("div", { className: cn(theme.timelineMonthStyle), style: { width: span.span * dayWidth }, children: span.label }, `${span.label}-${span.startIndex}`))) }), jsx("div", { className: "flex", style: { height: 28 }, children: days.map((day, i) => {
|
|
37
61
|
const isToday = isSameDay(day, today);
|
|
38
62
|
const weekend = isWeekend(day);
|
|
39
63
|
return (jsx("div", { className: cn(theme.timelineDayStyle, weekend && "bg-[var(--color-background-secondary)]/50", isToday && "font-semibold text-[var(--color-primary)]"), style: { width: dayWidth }, children: getDayLabel(day, zoom) }, i));
|
|
@@ -15,7 +15,7 @@ const ZOOM_OPTIONS = [
|
|
|
15
15
|
{ value: "year", label: "Year" },
|
|
16
16
|
];
|
|
17
17
|
const GanttToolbar = () => {
|
|
18
|
-
const { theme, zoom, setZoom, hideCompleted, setHideCompleted, onGroupAdd, todayOffset, dayWidth, readonly, } = useGanttContext();
|
|
18
|
+
const { theme, zoom, setZoom, hideCompleted, setHideCompleted, onGroupAdd, todayOffset, dayWidth, readonly, toolbar, } = useGanttContext();
|
|
19
19
|
const handleScrollToToday = () => {
|
|
20
20
|
const container = document.querySelector("[data-gantt-chart-scroll]");
|
|
21
21
|
if (!container || todayOffset === null)
|
|
@@ -23,7 +23,7 @@ const GanttToolbar = () => {
|
|
|
23
23
|
const targetScroll = todayOffset * dayWidth - container.clientWidth / 2;
|
|
24
24
|
container.scrollTo({ left: targetScroll, behavior: "smooth" });
|
|
25
25
|
};
|
|
26
|
-
return (jsxs("div", { className: cn(theme.toolbarStyle), children: [jsxs("div", { className: "flex items-center gap-
|
|
26
|
+
return (jsxs("div", { className: cn(theme.toolbarStyle), children: [jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [toolbar.today && todayOffset !== null && (jsxs(Button, { variant: "outline", color: "neutral", size: "sm", onClick: handleScrollToToday, children: [jsx(CalendarIcon, { className: "size-4" }), "Today"] })), toolbar.zoom && (jsx(Segmented, { value: zoom, onChange: (val) => setZoom(val), size: "sm", "aria-label": "Gantt zoom", className: "max-w-full overflow-x-auto", children: ZOOM_OPTIONS.map((opt) => (jsx(Segmented.Item, { value: opt.value, children: opt.label }, opt.value))) })), toolbar.hideCompleted && (jsx(Checkbox, { id: "gantt-hide-completed", name: "hideCompleted", value: "hideCompleted", checked: hideCompleted, onChange: (checked) => setHideCompleted(checked), label: "Hide completed", size: "sm" }))] }), jsx("div", { className: "flex items-center gap-2", children: toolbar.addGroup && onGroupAdd && !readonly && (jsxs(Button, { variant: "soft", size: "sm", onClick: onGroupAdd, children: [jsx(PlusIcon, { className: "size-3.5" }), "Add group"] })) })] }));
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
export { GanttToolbar };
|
|
@@ -34,7 +34,7 @@ function useGanttDrag({ dayWidth, mode, barWidthPx, originalProgress, disabled =
|
|
|
34
34
|
cancelledRef.current = false;
|
|
35
35
|
didDragRef.current = false;
|
|
36
36
|
const onPointerMove = (ev) => {
|
|
37
|
-
var _a;
|
|
37
|
+
var _a, _b;
|
|
38
38
|
if (cancelledRef.current)
|
|
39
39
|
return;
|
|
40
40
|
const rawDelta = ev.clientX - startXRef.current;
|
|
@@ -52,12 +52,13 @@ function useGanttDrag({ dayWidth, mode, barWidthPx, originalProgress, disabled =
|
|
|
52
52
|
const newProgress = Math.round(Math.max(0, Math.min(100, originalProgress + progressDelta)));
|
|
53
53
|
setDragProgress(newProgress);
|
|
54
54
|
setDragDeltaX(rawDelta);
|
|
55
|
+
(_a = onDragStateChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onDragStateChangeRef, true, rawDelta);
|
|
55
56
|
}
|
|
56
57
|
else {
|
|
57
58
|
// Move/resize: snap to day grid
|
|
58
59
|
const snappedDelta = Math.round(rawDelta / dayWidth) * dayWidth;
|
|
59
60
|
setDragDeltaX(snappedDelta);
|
|
60
|
-
(
|
|
61
|
+
(_b = onDragStateChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onDragStateChangeRef, true, snappedDelta);
|
|
61
62
|
}
|
|
62
63
|
};
|
|
63
64
|
const onPointerUp = () => {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { GanttChart } from "./GanttChart";
|
|
2
|
-
export type { GanttChartProps, GanttTask, GanttGroup, GanttColumn, GanttZoom, GanttHoliday, GanttChartThemeOverrides, } from "./GanttChart.types";
|
|
2
|
+
export type { GanttChartProps, GanttTask, GanttGroup, GanttColumn, GanttZoom, GanttTaskLabelMode, GanttToolbarOptions, GanttHoliday, GanttChartThemeOverrides, } from "./GanttChart.types";
|
|
3
3
|
export { ganttChartTheme } from "./GanttChart.theme";
|
|
@@ -55,12 +55,10 @@ const tableTheme = {
|
|
|
55
55
|
"shadow-lg shadow-[var(--color-neutral-200)]/50 " +
|
|
56
56
|
"dark:shadow-[var(--color-neutral-900)]/50",
|
|
57
57
|
// Utility controls - Column manager button
|
|
58
|
-
//
|
|
58
|
+
// Surface-like control; surface/border/hover tokens flip automatically between light/dark
|
|
59
59
|
columnManagerButton: "inline-flex items-center gap-2 rounded-[calc(var(--radius-base)*0.75)] px-3 py-2 text-sm font-medium " +
|
|
60
|
-
"focus:outline-none focus:ring-2 focus:ring-[var(--color-primary-500)] focus:ring-offset-2 " +
|
|
61
|
-
"
|
|
62
|
-
"bg-white border border-[var(--color-neutral-300)] text-[var(--color-text-primary)] hover:bg-[var(--color-neutral-50)] " +
|
|
63
|
-
"dark:bg-[var(--color-neutral-800)] dark:border-[var(--color-neutral-600)] dark:text-[var(--color-neutral-200)] dark:hover:bg-[var(--color-neutral-700)]",
|
|
60
|
+
"focus:outline-none focus:ring-2 focus:ring-[var(--color-primary-500)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface)] " +
|
|
61
|
+
"bg-[var(--color-surface)] border border-[var(--color-border)] text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)]",
|
|
64
62
|
columnManagerContainer: "mb-4 flex items-center justify-end gap-2",
|
|
65
63
|
};
|
|
66
64
|
|
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AuroraProps } from './Aurora.types';
|
|
3
3
|
/**
|
|
4
|
-
* Aurora creates
|
|
4
|
+
* Aurora creates a highly vibrant, WebGL-powered multi-color gradient background effect.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* and
|
|
6
|
+
* It uses a hardware-accelerated domain-warping Simplex Noise shader executing 100% on
|
|
7
|
+
* the GPU, achieving absolute 60/120 FPS butter-smooth rendering on all high-DPI screens.
|
|
8
|
+
* Features full support for custom blend modes and built-in stardust texture.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* The parent element should have `position: relative` and `overflow: hidden`.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* <div className="relative overflow-hidden h-96 bg-gray-950">
|
|
17
|
-
* <Aurora />
|
|
18
|
-
* <div className="relative z-10 text-white p-8">
|
|
19
|
-
* <h1>Hero Content</h1>
|
|
20
|
-
* </div>
|
|
21
|
-
* </div>
|
|
22
|
-
* ```
|
|
10
|
+
* If WebGL is unavailable or reduced motion is preferred, the component gracefully falls back
|
|
11
|
+
* to a gorgeous static CSS gradient layout to preserve SEO, styling, and accessibility.
|
|
23
12
|
*/
|
|
24
13
|
export declare const Aurora: React.FC<AuroraProps>;
|
|
@@ -1,158 +1,324 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
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
|
+
const DEFAULT_COLORS = ['#080f1a', '#0b2545', '#13d378', '#00f5ff'];
|
|
6
7
|
const clamp = (value, min, max) => {
|
|
7
8
|
if (Number.isNaN(value))
|
|
8
9
|
return min;
|
|
9
10
|
return Math.min(Math.max(value, min), max);
|
|
10
11
|
};
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* never synchronize, producing a natural morphing look.
|
|
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.
|
|
15
15
|
*/
|
|
16
|
-
const
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
`;
|
|
72
147
|
/**
|
|
73
|
-
* Aurora creates
|
|
148
|
+
* Aurora creates a highly vibrant, WebGL-powered multi-color gradient background effect.
|
|
74
149
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* and
|
|
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.
|
|
78
153
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* The parent element should have `position: relative` and `overflow: hidden`.
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* ```tsx
|
|
85
|
-
* <div className="relative overflow-hidden h-96 bg-gray-950">
|
|
86
|
-
* <Aurora />
|
|
87
|
-
* <div className="relative z-10 text-white p-8">
|
|
88
|
-
* <h1>Hero Content</h1>
|
|
89
|
-
* </div>
|
|
90
|
-
* </div>
|
|
91
|
-
* ```
|
|
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.
|
|
92
156
|
*/
|
|
93
|
-
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);
|
|
94
161
|
const shouldReduceMotion = useReducedMotion();
|
|
95
162
|
const safeColors = colors.length > 0 ? colors : DEFAULT_COLORS;
|
|
96
|
-
const safeBlur = Math.max(0, blur);
|
|
97
163
|
const safeSpeed = Math.max(0.1, speed);
|
|
98
164
|
const safeOpacity = clamp(opacity, 0, 1);
|
|
99
165
|
const safeSize = Math.max(0.1, size);
|
|
100
|
-
//
|
|
101
|
-
// Using percentage-based sizing so it scales with the container.
|
|
102
|
-
const blobDimension = `${40 * safeSize}%`;
|
|
103
|
-
const bleed = safeBlur * 1.5;
|
|
166
|
+
// Fallback Ambient CSS wash
|
|
104
167
|
const wash = safeColors
|
|
105
168
|
.slice(0, 4)
|
|
106
169
|
.map((color, index) => {
|
|
107
|
-
const stops = [
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
'75% 75%',
|
|
112
|
-
];
|
|
113
|
-
return `radial-gradient(circle at ${stops[index]}, ${color}55, transparent 42%)`;
|
|
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%)`;
|
|
114
174
|
})
|
|
115
175
|
.join(', ');
|
|
116
|
-
|
|
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;
|
|
203
|
+
}
|
|
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: {
|
|
117
310
|
...style,
|
|
118
|
-
}, "aria-hidden": "true", ...props, children:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
width: blobDimension,
|
|
130
|
-
height: blobDimension,
|
|
131
|
-
borderRadius: '9999px',
|
|
132
|
-
background: `radial-gradient(circle at center, ${color}, transparent 68%)`,
|
|
133
|
-
filter: `blur(${safeBlur}px)`,
|
|
134
|
-
mixBlendMode: 'screen',
|
|
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
|
-
})] }));
|
|
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
|
+
} })) }));
|
|
156
322
|
};
|
|
157
323
|
|
|
158
324
|
export { Aurora };
|