@morphika/andami 0.1.2
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/LICENSE +21 -0
- package/README.md +50 -0
- package/admin/assets.ts +4 -0
- package/admin/database.ts +4 -0
- package/admin/index.ts +6 -0
- package/admin/login.ts +4 -0
- package/admin/navigation.ts +4 -0
- package/admin/pages-editor.ts +4 -0
- package/admin/pages.ts +4 -0
- package/admin/projects-editor.ts +4 -0
- package/admin/projects.ts +4 -0
- package/admin/settings.ts +4 -0
- package/admin/setup.ts +4 -0
- package/admin/storage.ts +4 -0
- package/admin/styles.ts +4 -0
- package/app/(site)/[slug]/loading.tsx +20 -0
- package/app/(site)/[slug]/page.tsx +83 -0
- package/app/(site)/error.tsx +32 -0
- package/app/(site)/layout.tsx +53 -0
- package/app/(site)/loading.tsx +20 -0
- package/app/(site)/not-found.tsx +41 -0
- package/app/(site)/page.tsx +43 -0
- package/app/(site)/preview/page.tsx +99 -0
- package/app/(site)/work/[slug]/loading.tsx +23 -0
- package/app/(site)/work/[slug]/page.tsx +84 -0
- package/app/admin/assets/page.tsx +573 -0
- package/app/admin/database/page.tsx +302 -0
- package/app/admin/error.tsx +53 -0
- package/app/admin/layout.tsx +273 -0
- package/app/admin/login/page.tsx +88 -0
- package/app/admin/navigation/page.tsx +157 -0
- package/app/admin/page.tsx +17 -0
- package/app/admin/pages/[slug]/page.tsx +849 -0
- package/app/admin/pages/page.tsx +588 -0
- package/app/admin/projects/[slug]/page.tsx +3 -0
- package/app/admin/projects/page.tsx +669 -0
- package/app/admin/settings/page.tsx +132 -0
- package/app/admin/setup/page.tsx +64 -0
- package/app/admin/storage/page.tsx +518 -0
- package/app/admin/styles/page.tsx +243 -0
- package/app/api/admin/assets/file/route.ts +81 -0
- package/app/api/admin/assets/health/route.ts +170 -0
- package/app/api/admin/assets/register/route.ts +163 -0
- package/app/api/admin/assets/registry/route.ts +98 -0
- package/app/api/admin/assets/relink/confirm/route.ts +242 -0
- package/app/api/admin/assets/relink/route.ts +202 -0
- package/app/api/admin/assets/scan/route.ts +271 -0
- package/app/api/admin/auth/route.ts +160 -0
- package/app/api/admin/custom-sections/[slug]/route.ts +159 -0
- package/app/api/admin/custom-sections/route.ts +127 -0
- package/app/api/admin/database/route.ts +53 -0
- package/app/api/admin/pages/[slug]/duplicate/route.ts +91 -0
- package/app/api/admin/pages/[slug]/route.ts +617 -0
- package/app/api/admin/pages/[slug]/set-home/route.ts +76 -0
- package/app/api/admin/pages/route.ts +129 -0
- package/app/api/admin/preview/route.ts +53 -0
- package/app/api/admin/r2/connect/route.ts +181 -0
- package/app/api/admin/r2/delete/route.ts +198 -0
- package/app/api/admin/r2/disconnect/route.ts +42 -0
- package/app/api/admin/r2/rename/route.ts +265 -0
- package/app/api/admin/r2/status/route.ts +106 -0
- package/app/api/admin/r2/upload-url/route.ts +148 -0
- package/app/api/admin/revalidate/route.ts +55 -0
- package/app/api/admin/settings/route.ts +279 -0
- package/app/api/admin/setup/complete/route.ts +51 -0
- package/app/api/admin/setup/route.ts +118 -0
- package/app/api/admin/storage/switch/route.ts +117 -0
- package/app/api/admin/styles/fonts/route.ts +97 -0
- package/app/api/admin/styles/route.ts +304 -0
- package/app/api/assets/[...path]/route.ts +98 -0
- package/app/api/custom-sections/[id]/route.ts +43 -0
- package/app/api/draft-mode/disable/route.ts +10 -0
- package/app/api/draft-mode/enable/route.ts +26 -0
- package/app/api/projects/route.ts +42 -0
- package/app/api/styles/route.ts +88 -0
- package/app/favicon.ico +0 -0
- package/app/globals.css +7 -0
- package/app/layout.tsx +53 -0
- package/app/robots.ts +17 -0
- package/app/sitemap.ts +48 -0
- package/app/studio/[[...index]]/page.tsx +8 -0
- package/components/admin/MetadataEditor.tsx +173 -0
- package/components/admin/PublishToggle.tsx +130 -0
- package/components/admin/icons.tsx +40 -0
- package/components/admin/nav-builder/NavBuilder.tsx +182 -0
- package/components/admin/nav-builder/NavBuilderGrid.tsx +326 -0
- package/components/admin/nav-builder/NavGeneralSettings.tsx +275 -0
- package/components/admin/nav-builder/NavGridCell.tsx +48 -0
- package/components/admin/nav-builder/NavGridItem.tsx +189 -0
- package/components/admin/nav-builder/NavItemSettings.tsx +288 -0
- package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -0
- package/components/admin/nav-builder/NavLivePreview.tsx +125 -0
- package/components/admin/nav-builder/NavSettingsFields.tsx +248 -0
- package/components/admin/nav-builder/NavSettingsPanel.tsx +127 -0
- package/components/admin/nav-builder/index.ts +10 -0
- package/components/admin/nav-builder/nav-builder-utils.ts +238 -0
- package/components/admin/setup-wizard/BrandingStep.tsx +218 -0
- package/components/admin/setup-wizard/DatabaseStep.tsx +331 -0
- package/components/admin/setup-wizard/DoneStep.tsx +187 -0
- package/components/admin/setup-wizard/SetupWizard.tsx +166 -0
- package/components/admin/setup-wizard/StorageStep.tsx +308 -0
- package/components/admin/setup-wizard/WelcomeStep.tsx +96 -0
- package/components/admin/setup-wizard/index.ts +9 -0
- package/components/admin/styles/ColorsEditor.tsx +214 -0
- package/components/admin/styles/FontsEditor.tsx +258 -0
- package/components/admin/styles/GridLayoutEditor.tsx +292 -0
- package/components/admin/styles/LinksButtonsEditor.tsx +120 -0
- package/components/admin/styles/TypographyEditor.tsx +266 -0
- package/components/admin/styles/index.ts +9 -0
- package/components/admin/styles/shared.tsx +68 -0
- package/components/blocks/BlockRenderer.tsx +404 -0
- package/components/blocks/ButtonBlockRenderer.tsx +52 -0
- package/components/blocks/CoverBlockRenderer.tsx +239 -0
- package/components/blocks/CustomSectionInstanceRenderer.tsx +82 -0
- package/components/blocks/EnterAnimationWrapper.tsx +140 -0
- package/components/blocks/HoverAnimationWrapper.tsx +308 -0
- package/components/blocks/ImageBlockRenderer.tsx +61 -0
- package/components/blocks/ImageGridBlockRenderer.tsx +545 -0
- package/components/blocks/PageBackground.tsx +28 -0
- package/components/blocks/PageNavAnimation.tsx +35 -0
- package/components/blocks/PageNavColor.tsx +24 -0
- package/components/blocks/PageRenderer.tsx +142 -0
- package/components/blocks/ParallaxGroupRenderer.tsx +448 -0
- package/components/blocks/ParallaxSlideRenderer.tsx +175 -0
- package/components/blocks/ProjectGridBlockRenderer.tsx +556 -0
- package/components/blocks/SectionRenderer.tsx +170 -0
- package/components/blocks/SectionV2Renderer.tsx +330 -0
- package/components/blocks/ShaderCanvas.tsx +392 -0
- package/components/blocks/SpacerBlockRenderer.tsx +17 -0
- package/components/blocks/TextBlockRenderer.tsx +87 -0
- package/components/blocks/TypewriterRichText.tsx +464 -0
- package/components/blocks/TypewriterWrapper.tsx +149 -0
- package/components/blocks/VideoBlockRenderer.tsx +304 -0
- package/components/blocks/index.ts +2 -0
- package/components/builder/AssetBrowser.tsx +2 -0
- package/components/builder/BlockLivePreview.tsx +101 -0
- package/components/builder/BlockTypePicker.tsx +178 -0
- package/components/builder/BuilderCanvas.tsx +354 -0
- package/components/builder/CanvasMinimap.tsx +200 -0
- package/components/builder/CanvasToolbar.tsx +202 -0
- package/components/builder/ColorPicker.tsx +243 -0
- package/components/builder/ColorSwatchPicker.tsx +274 -0
- package/components/builder/ColumnDragContext.tsx +51 -0
- package/components/builder/ColumnDragOverlay.tsx +110 -0
- package/components/builder/CustomSectionInstanceCard.tsx +97 -0
- package/components/builder/DeviceFrame.tsx +123 -0
- package/components/builder/DndWrapper.tsx +337 -0
- package/components/builder/InsertionLines.tsx +186 -0
- package/components/builder/ParallaxGroupCanvas.tsx +228 -0
- package/components/builder/ParallaxSlideHeader.tsx +113 -0
- package/components/builder/ReadOnlyFrame.tsx +417 -0
- package/components/builder/SectionEditorBar.tsx +288 -0
- package/components/builder/SectionTypePicker.tsx +422 -0
- package/components/builder/SectionV2Canvas.tsx +297 -0
- package/components/builder/SectionV2Column.tsx +488 -0
- package/components/builder/SettingsPanel.tsx +911 -0
- package/components/builder/SortableBlock.tsx +230 -0
- package/components/builder/SortableRow.tsx +362 -0
- package/components/builder/VirtualAssetGrid.tsx +397 -0
- package/components/builder/asset-browser/AssetBrowser.tsx +178 -0
- package/components/builder/asset-browser/FileLightbox.tsx +116 -0
- package/components/builder/asset-browser/FolderTreeItem.tsx +55 -0
- package/components/builder/asset-browser/R2BrowserContent.tsx +436 -0
- package/components/builder/asset-browser/R2ContextMenu.tsx +98 -0
- package/components/builder/asset-browser/VideoThumbnail.tsx +63 -0
- package/components/builder/asset-browser/helpers.ts +88 -0
- package/components/builder/asset-browser/index.ts +1 -0
- package/components/builder/asset-browser/types.ts +49 -0
- package/components/builder/asset-browser/useAssetBrowser.ts +344 -0
- package/components/builder/asset-browser/useR2DragDrop.ts +116 -0
- package/components/builder/asset-browser/useR2Operations.ts +189 -0
- package/components/builder/blockStyles.tsx +295 -0
- package/components/builder/editors/ButtonBlockEditor.tsx +184 -0
- package/components/builder/editors/CoverBlockEditor.tsx +488 -0
- package/components/builder/editors/EnterAnimationPicker.tsx +297 -0
- package/components/builder/editors/HoverEffectPicker.tsx +209 -0
- package/components/builder/editors/ImageBlockEditor.tsx +206 -0
- package/components/builder/editors/ImageGridBlockEditor.tsx +386 -0
- package/components/builder/editors/ProjectGridEditor.tsx +648 -0
- package/components/builder/editors/SpacerBlockEditor.tsx +167 -0
- package/components/builder/editors/StaggerSettings.tsx +108 -0
- package/components/builder/editors/TextAlignmentIcons.tsx +39 -0
- package/components/builder/editors/TextBlockEditor.tsx +462 -0
- package/components/builder/editors/TextStylePicker.tsx +183 -0
- package/components/builder/editors/VideoBlockEditor.tsx +278 -0
- package/components/builder/editors/index.ts +10 -0
- package/components/builder/editors/shared.tsx +345 -0
- package/components/builder/hooks/useColumnDrag.ts +472 -0
- package/components/builder/hooks/useColumnResize.ts +221 -0
- package/components/builder/index.ts +12 -0
- package/components/builder/live-preview/LiveButtonPreview.tsx +38 -0
- package/components/builder/live-preview/LiveCoverPreview.tsx +146 -0
- package/components/builder/live-preview/LiveImageGridPreview.tsx +123 -0
- package/components/builder/live-preview/LiveImagePreview.tsx +107 -0
- package/components/builder/live-preview/LiveProjectGridPreview.tsx +1010 -0
- package/components/builder/live-preview/LiveSpacerPreview.tsx +9 -0
- package/components/builder/live-preview/LiveTextEditor.tsx +198 -0
- package/components/builder/live-preview/LiveVideoPreview.tsx +98 -0
- package/components/builder/live-preview/index.ts +10 -0
- package/components/builder/live-preview/shared.tsx +153 -0
- package/components/builder/settings-panel/BlockLayoutTab.tsx +532 -0
- package/components/builder/settings-panel/BlockSettings.tsx +94 -0
- package/components/builder/settings-panel/ColumnV2Settings.tsx +160 -0
- package/components/builder/settings-panel/LayoutTab.tsx +310 -0
- package/components/builder/settings-panel/PageSettings.tsx +200 -0
- package/components/builder/settings-panel/ParallaxGroupSettings.tsx +118 -0
- package/components/builder/settings-panel/ParallaxSlideSettings.tsx +178 -0
- package/components/builder/settings-panel/SectionV2AnimationTab.tsx +103 -0
- package/components/builder/settings-panel/SectionV2LayoutTab.tsx +312 -0
- package/components/builder/settings-panel/SectionV2Settings.tsx +323 -0
- package/components/builder/settings-panel/TRBLInputs.tsx +51 -0
- package/components/builder/settings-panel/index.ts +19 -0
- package/components/builder/settings-panel/responsive-helpers.ts +524 -0
- package/components/ui/CustomCursor.tsx +118 -0
- package/components/ui/NavContentLightbox.tsx +152 -0
- package/components/ui/Navbar.tsx +582 -0
- package/components/ui/PortfolioTracker.tsx +87 -0
- package/components/ui/ScrollToTop.tsx +47 -0
- package/lib/animation/enter-presets.ts +147 -0
- package/lib/animation/enter-resolve.ts +90 -0
- package/lib/animation/enter-types.ts +128 -0
- package/lib/animation/hover-effect-presets.ts +210 -0
- package/lib/animation/hover-effect-types.ts +126 -0
- package/lib/asset-retry.ts +111 -0
- package/lib/assets.ts +92 -0
- package/lib/audit.ts +35 -0
- package/lib/auth-token.ts +94 -0
- package/lib/auth.ts +13 -0
- package/lib/builder/cascade-helpers.ts +51 -0
- package/lib/builder/cascade.ts +533 -0
- package/lib/builder/constants.ts +103 -0
- package/lib/builder/defaults.ts +182 -0
- package/lib/builder/history.ts +48 -0
- package/lib/builder/index.ts +21 -0
- package/lib/builder/layout-styles.ts +344 -0
- package/lib/builder/masonry.ts +166 -0
- package/lib/builder/responsive.ts +156 -0
- package/lib/builder/serializer.ts +845 -0
- package/lib/builder/store-blocks.ts +193 -0
- package/lib/builder/store-canvas.ts +319 -0
- package/lib/builder/store-helpers.ts +490 -0
- package/lib/builder/store-sections.ts +709 -0
- package/lib/builder/store.ts +333 -0
- package/lib/builder/templates.ts +297 -0
- package/lib/builder/types.ts +374 -0
- package/lib/builder/utils.ts +37 -0
- package/lib/color-utils.ts +116 -0
- package/lib/config/index.ts +57 -0
- package/lib/config/types.ts +122 -0
- package/lib/contexts/AssetContext.tsx +79 -0
- package/lib/contexts/NavAnimationContext.tsx +44 -0
- package/lib/contexts/NavColorContext.tsx +38 -0
- package/lib/contexts/PageExitContext.tsx +194 -0
- package/lib/contexts/ThumbStatusContext.tsx +83 -0
- package/lib/csrf-client.ts +34 -0
- package/lib/csrf.ts +68 -0
- package/lib/format-utils.ts +24 -0
- package/lib/hooks/useViewport.ts +42 -0
- package/lib/logger.ts +81 -0
- package/lib/revalidate.ts +23 -0
- package/lib/sanitize.ts +91 -0
- package/lib/sanity/client.ts +8 -0
- package/lib/sanity/queries.ts +486 -0
- package/lib/sanity/types.ts +869 -0
- package/lib/sanity/writeClient.ts +24 -0
- package/lib/security.ts +402 -0
- package/lib/setup/detect.ts +156 -0
- package/lib/shader/glsl/index.ts +27 -0
- package/lib/shader/glsl/pixelate.ts +51 -0
- package/lib/shader/glsl/rgb-shift.ts +45 -0
- package/lib/shader/glsl/ripple.ts +46 -0
- package/lib/shader/glsl/vertex.ts +14 -0
- package/lib/storage/index.ts +211 -0
- package/lib/storage/r2-adapter.ts +286 -0
- package/lib/storage/types.ts +125 -0
- package/lib/styles/provider.tsx +267 -0
- package/lib/thumbnails/generate.ts +151 -0
- package/lib/utils.ts +6 -0
- package/package.json +212 -0
- package/sanity/compose.ts +65 -0
- package/sanity/sanity.config.ts +126 -0
- package/sanity/schemas/assetRegistry.ts +301 -0
- package/sanity/schemas/blocks/blockLayout.ts +90 -0
- package/sanity/schemas/blocks/buttonBlock.ts +82 -0
- package/sanity/schemas/blocks/coverBlock.ts +229 -0
- package/sanity/schemas/blocks/imageBlock.ts +58 -0
- package/sanity/schemas/blocks/imageGridBlock.ts +112 -0
- package/sanity/schemas/blocks/index.ts +9 -0
- package/sanity/schemas/blocks/projectGridBlock.ts +251 -0
- package/sanity/schemas/blocks/spacerBlock.ts +41 -0
- package/sanity/schemas/blocks/textBlock.ts +139 -0
- package/sanity/schemas/blocks/videoBlock.ts +80 -0
- package/sanity/schemas/customSection.ts +69 -0
- package/sanity/schemas/customSectionInstance.ts +163 -0
- package/sanity/schemas/index.ts +111 -0
- package/sanity/schemas/objects/enterAnimationConfig.ts +72 -0
- package/sanity/schemas/objects/hoverEffectConfig.ts +90 -0
- package/sanity/schemas/objects/parallaxGroup.ts +66 -0
- package/sanity/schemas/objects/parallaxSlide.ts +217 -0
- package/sanity/schemas/objects/typewriterConfig.ts +38 -0
- package/sanity/schemas/page.ts +162 -0
- package/sanity/schemas/pageSection.ts +157 -0
- package/sanity/schemas/pageSectionV2.ts +269 -0
- package/sanity/schemas/siteSettings.ts +256 -0
- package/sanity/schemas/siteStyles.ts +212 -0
- package/site/error.ts +4 -0
- package/site/index.ts +8 -0
- package/site/not-found.ts +4 -0
- package/site/page.ts +4 -0
- package/site/preview.ts +4 -0
- package/site/robots.ts +4 -0
- package/site/sitemap.ts +4 -0
- package/site/work.ts +4 -0
- package/studio/index.ts +4 -0
- package/styles/admin.css +85 -0
- package/styles/animations.css +237 -0
- package/styles/base.css +148 -0
- package/styles/globals.css +10 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from "react";
|
|
4
|
+
|
|
5
|
+
export default function ScrollToTop() {
|
|
6
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
7
|
+
|
|
8
|
+
const handleScroll = useCallback(() => {
|
|
9
|
+
setIsVisible(window.scrollY > 400);
|
|
10
|
+
}, []);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
14
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
15
|
+
}, [handleScroll]);
|
|
16
|
+
|
|
17
|
+
const scrollToTop = () => {
|
|
18
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<button
|
|
23
|
+
onClick={scrollToTop}
|
|
24
|
+
className={`fixed bottom-8 right-8 z-50 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 hover:bg-white/20 transition-all duration-300 ${
|
|
25
|
+
isVisible
|
|
26
|
+
? "opacity-100 translate-y-0"
|
|
27
|
+
: "opacity-0 translate-y-4 pointer-events-none"
|
|
28
|
+
}`}
|
|
29
|
+
aria-label="Scroll to top"
|
|
30
|
+
>
|
|
31
|
+
{/* Chevron up */}
|
|
32
|
+
<svg
|
|
33
|
+
width="28"
|
|
34
|
+
height="28"
|
|
35
|
+
viewBox="0 0 24 24"
|
|
36
|
+
fill="none"
|
|
37
|
+
stroke="currentColor"
|
|
38
|
+
strokeWidth="1.5"
|
|
39
|
+
strokeLinecap="round"
|
|
40
|
+
strokeLinejoin="round"
|
|
41
|
+
className="text-brand-text"
|
|
42
|
+
>
|
|
43
|
+
<polyline points="18 15 12 9 6 15" />
|
|
44
|
+
</svg>
|
|
45
|
+
</button>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enter Animation Presets
|
|
3
|
+
*
|
|
4
|
+
* Metadata + keyframe definitions for each enter animation preset.
|
|
5
|
+
* Used by EnterAnimationWrapper (public site) and EnterAnimationPicker (builder UI).
|
|
6
|
+
*
|
|
7
|
+
* Session 116 — Animation UX Refactor.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
EnterPreset,
|
|
12
|
+
AnimationEasing,
|
|
13
|
+
EnterAnimationConfig,
|
|
14
|
+
} from "./enter-types";
|
|
15
|
+
import { ENTER_DEFAULTS } from "./enter-types";
|
|
16
|
+
|
|
17
|
+
// ── Default config ─────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_ENTER_ANIMATION: Required<EnterAnimationConfig> = {
|
|
20
|
+
preset: "none",
|
|
21
|
+
duration: ENTER_DEFAULTS.duration,
|
|
22
|
+
delay: ENTER_DEFAULTS.delay,
|
|
23
|
+
easing: ENTER_DEFAULTS.easing,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ── Preset metadata (for builder UI dropdowns) ─────────────────────
|
|
27
|
+
|
|
28
|
+
export interface EnterPresetInfo {
|
|
29
|
+
id: EnterPreset;
|
|
30
|
+
label: string;
|
|
31
|
+
description: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Human-readable metadata for every enter preset.
|
|
36
|
+
* Ordered for UI display. Generic presets first, then block-specific.
|
|
37
|
+
*/
|
|
38
|
+
export const ENTER_PRESET_INFO: EnterPresetInfo[] = [
|
|
39
|
+
// Generic presets
|
|
40
|
+
{ id: "none", label: "None", description: "No animation" },
|
|
41
|
+
{ id: "fade", label: "Fade", description: "Opacity reveal" },
|
|
42
|
+
{ id: "slide-up", label: "Slide Up", description: "Slide in from below" },
|
|
43
|
+
{ id: "slide-down", label: "Slide Down", description: "Slide in from above" },
|
|
44
|
+
{ id: "scale", label: "Scale", description: "Subtle zoom-in" },
|
|
45
|
+
{ id: "blur", label: "Blur", description: "Blur dissolve reveal" },
|
|
46
|
+
{ id: "reveal", label: "Reveal", description: "Clip-path wipe" },
|
|
47
|
+
// Block-specific presets
|
|
48
|
+
{ id: "typewriter", label: "Typewriter", description: "Character-by-character typing" },
|
|
49
|
+
{ id: "blur-in", label: "Blur In", description: "Text blur dissolve" },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Quick lookup: preset ID → metadata.
|
|
54
|
+
*/
|
|
55
|
+
export const ENTER_PRESET_MAP: Record<EnterPreset, EnterPresetInfo> = Object.fromEntries(
|
|
56
|
+
ENTER_PRESET_INFO.map((p) => [p.id, p])
|
|
57
|
+
) as Record<EnterPreset, EnterPresetInfo>;
|
|
58
|
+
|
|
59
|
+
// ── Easing options (for builder UI) ────────────────────────────────
|
|
60
|
+
|
|
61
|
+
export const ENTER_EASINGS: { id: AnimationEasing; label: string }[] = [
|
|
62
|
+
{ id: "ease-out", label: "Ease Out" },
|
|
63
|
+
{ id: "ease", label: "Ease" },
|
|
64
|
+
{ id: "ease-in", label: "Ease In" },
|
|
65
|
+
{ id: "ease-in-out", label: "Ease In Out" },
|
|
66
|
+
{ id: "linear", label: "Linear" },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// ── Duration / delay ranges (for builder UI sliders) ───────────────
|
|
70
|
+
|
|
71
|
+
export const DURATION_RANGE = { min: 100, max: 5000, step: 50, default: 600 } as const;
|
|
72
|
+
export const DELAY_RANGE = { min: 0, max: 5000, step: 50, default: 0 } as const;
|
|
73
|
+
|
|
74
|
+
// ── Keyframe definitions ───────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
export interface PresetKeyframes {
|
|
77
|
+
/** CSS properties at animation start (hidden state) */
|
|
78
|
+
from: Record<string, string>;
|
|
79
|
+
/** CSS properties at animation end (visible state) */
|
|
80
|
+
to: Record<string, string>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Returns the CSS keyframe from/to values for an enter preset.
|
|
85
|
+
*
|
|
86
|
+
* Intensity is removed in the new system — presets use fixed,
|
|
87
|
+
* sensible transform values.
|
|
88
|
+
*
|
|
89
|
+
* Returns null for "none" and for presets handled by custom
|
|
90
|
+
* renderers (typewriter).
|
|
91
|
+
*/
|
|
92
|
+
export function getEnterKeyframes(preset: EnterPreset): PresetKeyframes | null {
|
|
93
|
+
switch (preset) {
|
|
94
|
+
case "none":
|
|
95
|
+
return null;
|
|
96
|
+
|
|
97
|
+
case "fade":
|
|
98
|
+
return {
|
|
99
|
+
from: { opacity: "0" },
|
|
100
|
+
to: { opacity: "1" },
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
case "slide-up":
|
|
104
|
+
return {
|
|
105
|
+
from: { opacity: "0", transform: "translateY(30px)" },
|
|
106
|
+
to: { opacity: "1", transform: "translateY(0)" },
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
case "slide-down":
|
|
110
|
+
return {
|
|
111
|
+
from: { opacity: "0", transform: "translateY(-30px)" },
|
|
112
|
+
to: { opacity: "1", transform: "translateY(0)" },
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
case "scale":
|
|
116
|
+
return {
|
|
117
|
+
from: { opacity: "0", transform: "scale(0.85)" },
|
|
118
|
+
to: { opacity: "1", transform: "scale(1)" },
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
case "blur":
|
|
122
|
+
return {
|
|
123
|
+
from: { opacity: "0", filter: "blur(10px)" },
|
|
124
|
+
to: { opacity: "1", filter: "blur(0px)" },
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
case "blur-in":
|
|
128
|
+
// Similar to blur but tuned for text — lighter blur, no opacity
|
|
129
|
+
return {
|
|
130
|
+
from: { opacity: "0", filter: "blur(6px)" },
|
|
131
|
+
to: { opacity: "1", filter: "blur(0px)" },
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
case "reveal":
|
|
135
|
+
return {
|
|
136
|
+
from: { opacity: "0", clipPath: "inset(0 100% 0 0)" },
|
|
137
|
+
to: { opacity: "1", clipPath: "inset(0 0% 0 0)" },
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
case "typewriter":
|
|
141
|
+
// Handled by TypewriterWrapper, not CSS keyframes
|
|
142
|
+
return null;
|
|
143
|
+
|
|
144
|
+
default:
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enter Animation Cascade Resolution
|
|
3
|
+
*
|
|
4
|
+
* 4-level cascade: block → column → section → page → null.
|
|
5
|
+
* Full override at each level — no field-level merging.
|
|
6
|
+
*
|
|
7
|
+
* Session 116 — Animation UX Refactor.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
EnterAnimationConfig,
|
|
12
|
+
ResolvedEnterAnimationConfig,
|
|
13
|
+
} from "./enter-types";
|
|
14
|
+
import { ENTER_DEFAULTS } from "./enter-types";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolves the effective enter animation for a block.
|
|
18
|
+
*
|
|
19
|
+
* Cascade order (first defined wins):
|
|
20
|
+
* 1. Block-level enter_animation
|
|
21
|
+
* 2. Column-level enter_animation
|
|
22
|
+
* 3. Section-level enter_animation
|
|
23
|
+
* 4. Page-level enter_animation
|
|
24
|
+
* 5. null (no animation)
|
|
25
|
+
*
|
|
26
|
+
* Full override semantics — if a level defines any property,
|
|
27
|
+
* that entire config is used (no merging with parent levels).
|
|
28
|
+
*
|
|
29
|
+
* Returns null if no level has an animation configured.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveEnterAnimation(
|
|
32
|
+
blockConfig?: EnterAnimationConfig,
|
|
33
|
+
columnConfig?: EnterAnimationConfig,
|
|
34
|
+
sectionConfig?: EnterAnimationConfig,
|
|
35
|
+
pageConfig?: EnterAnimationConfig,
|
|
36
|
+
): ResolvedEnterAnimationConfig | null {
|
|
37
|
+
// Block-level wins
|
|
38
|
+
if (blockConfig && isConfigured(blockConfig)) {
|
|
39
|
+
return normalizeEnterConfig(blockConfig);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Column-level
|
|
43
|
+
if (columnConfig && isConfigured(columnConfig)) {
|
|
44
|
+
return normalizeEnterConfig(columnConfig);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Section-level
|
|
48
|
+
if (sectionConfig && isConfigured(sectionConfig)) {
|
|
49
|
+
return normalizeEnterConfig(sectionConfig);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Page-level
|
|
53
|
+
if (pageConfig && isConfigured(pageConfig)) {
|
|
54
|
+
return normalizeEnterConfig(pageConfig);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// No animation anywhere
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if a config has any meaningful property set.
|
|
63
|
+
* A config with only undefined values is treated as "not configured".
|
|
64
|
+
*/
|
|
65
|
+
function isConfigured(config: EnterAnimationConfig): boolean {
|
|
66
|
+
return (
|
|
67
|
+
config.preset !== undefined ||
|
|
68
|
+
config.duration !== undefined ||
|
|
69
|
+
config.delay !== undefined ||
|
|
70
|
+
config.easing !== undefined
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Fill defaults for any missing fields in a partial config.
|
|
76
|
+
* Returns a fully resolved config with all fields present.
|
|
77
|
+
*
|
|
78
|
+
* If preset is "none", still resolves — the wrapper component
|
|
79
|
+
* will detect "none" and skip rendering.
|
|
80
|
+
*/
|
|
81
|
+
export function normalizeEnterConfig(
|
|
82
|
+
config: EnterAnimationConfig,
|
|
83
|
+
): ResolvedEnterAnimationConfig {
|
|
84
|
+
return {
|
|
85
|
+
preset: config.preset ?? "none",
|
|
86
|
+
duration: config.duration ?? ENTER_DEFAULTS.duration,
|
|
87
|
+
delay: config.delay ?? ENTER_DEFAULTS.delay,
|
|
88
|
+
easing: config.easing ?? ENTER_DEFAULTS.easing,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enter Animation System — Type definitions
|
|
3
|
+
*
|
|
4
|
+
* Replaces the old scroll/enter/load trigger system with a unified "enter"
|
|
5
|
+
* behavior: IntersectionObserver with above-the-fold detection.
|
|
6
|
+
*
|
|
7
|
+
* Session 116 — Animation UX Refactor.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ContentBlock } from "../../lib/sanity/types";
|
|
11
|
+
|
|
12
|
+
// ── Block type alias (mirrors builder types.ts) ────────────────────
|
|
13
|
+
|
|
14
|
+
type BlockType = ContentBlock["_type"];
|
|
15
|
+
|
|
16
|
+
// ── Easing ─────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export type AnimationEasing =
|
|
19
|
+
| "linear"
|
|
20
|
+
| "ease"
|
|
21
|
+
| "ease-in"
|
|
22
|
+
| "ease-out"
|
|
23
|
+
| "ease-in-out";
|
|
24
|
+
|
|
25
|
+
// ── Generic enter presets (page / section / column) ────────────────
|
|
26
|
+
|
|
27
|
+
export type GenericEnterPreset =
|
|
28
|
+
| "none"
|
|
29
|
+
| "fade"
|
|
30
|
+
| "slide-up"
|
|
31
|
+
| "slide-down"
|
|
32
|
+
| "scale"
|
|
33
|
+
| "blur"
|
|
34
|
+
| "reveal";
|
|
35
|
+
|
|
36
|
+
/** Array of generic enter presets for builder UI dropdowns */
|
|
37
|
+
export const GENERIC_ENTER_PRESETS: GenericEnterPreset[] = [
|
|
38
|
+
"none",
|
|
39
|
+
"fade",
|
|
40
|
+
"slide-up",
|
|
41
|
+
"slide-down",
|
|
42
|
+
"scale",
|
|
43
|
+
"blur",
|
|
44
|
+
"reveal",
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// ── Block-specific enter presets ───────────────────────────────────
|
|
48
|
+
|
|
49
|
+
/** Presets exclusive to specific block types */
|
|
50
|
+
export type BlockSpecificEnterPreset =
|
|
51
|
+
| "typewriter" // textBlock only
|
|
52
|
+
| "blur-in"; // textBlock only (separate from generic "blur")
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Union of ALL enter presets — generic + block-specific.
|
|
56
|
+
* Used as the field type in EnterAnimationConfig so any preset
|
|
57
|
+
* can be stored regardless of context.
|
|
58
|
+
*/
|
|
59
|
+
export type EnterPreset = GenericEnterPreset | BlockSpecificEnterPreset;
|
|
60
|
+
|
|
61
|
+
// ── Per-block-type preset registry ─────────────────────────────────
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Which enter presets are available for each block type.
|
|
65
|
+
* Block types not listed here have no block-specific enter animation
|
|
66
|
+
* (they rely on the generic cascade from column/section/page).
|
|
67
|
+
*
|
|
68
|
+
* Based on the registry table in ANIMATION-UX-ROADMAP.md.
|
|
69
|
+
*/
|
|
70
|
+
export const BLOCK_ENTER_PRESETS: Record<BlockType, readonly EnterPreset[]> = {
|
|
71
|
+
textBlock: ["typewriter", "fade", "blur-in", "slide-up"],
|
|
72
|
+
imageBlock: ["fade", "blur", "reveal", "scale", "slide-up"],
|
|
73
|
+
imageGridBlock: ["fade", "scale", "slide-up"],
|
|
74
|
+
videoBlock: ["fade", "slide-up"],
|
|
75
|
+
buttonBlock: ["fade", "slide-up", "scale"],
|
|
76
|
+
coverBlock: ["fade", "blur", "reveal", "scale"],
|
|
77
|
+
spacerBlock: [], // invisible — no animation
|
|
78
|
+
projectGridBlock: [], // uses card_entrance system
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// ── Enter animation config ─────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Stored in Sanity on pages, sections, columns, and blocks.
|
|
85
|
+
* All fields optional — consumers use resolveEnterAnimation()
|
|
86
|
+
* or fill defaults at the call-site.
|
|
87
|
+
*/
|
|
88
|
+
export interface EnterAnimationConfig {
|
|
89
|
+
preset?: EnterPreset;
|
|
90
|
+
duration?: number; // ms, default 600
|
|
91
|
+
delay?: number; // ms, default 0
|
|
92
|
+
easing?: AnimationEasing; // default "ease-out"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Fully resolved config — all fields guaranteed present.
|
|
97
|
+
* Returned by resolveEnterAnimation().
|
|
98
|
+
*/
|
|
99
|
+
export interface ResolvedEnterAnimationConfig {
|
|
100
|
+
preset: EnterPreset;
|
|
101
|
+
duration: number;
|
|
102
|
+
delay: number;
|
|
103
|
+
easing: AnimationEasing;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── Typewriter config (textBlock only) ─────────────────────────────
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Extra config for the "typewriter" enter preset.
|
|
110
|
+
* Stored on textBlock.typewriter_config when preset === "typewriter".
|
|
111
|
+
*/
|
|
112
|
+
export interface TypewriterConfig {
|
|
113
|
+
speed?: number; // chars/sec, default 40, range 10–100
|
|
114
|
+
mode?: "character" | "word"; // default "character"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Defaults ───────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
export const ENTER_DEFAULTS = {
|
|
120
|
+
duration: 600,
|
|
121
|
+
delay: 0,
|
|
122
|
+
easing: "ease-out" as AnimationEasing,
|
|
123
|
+
} as const;
|
|
124
|
+
|
|
125
|
+
export const TYPEWRITER_DEFAULTS: Required<TypewriterConfig> = {
|
|
126
|
+
speed: 40,
|
|
127
|
+
mode: "character",
|
|
128
|
+
} as const;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hover Effect Presets
|
|
3
|
+
*
|
|
4
|
+
* Unified registry of CSS-based and shader-based hover effects.
|
|
5
|
+
* The builder UI uses this for dropdowns. The public site uses
|
|
6
|
+
* getHoverEffectStyles() for CSS hovers and delegates to
|
|
7
|
+
* ShaderCanvas for shader hovers.
|
|
8
|
+
*
|
|
9
|
+
* Session 116 — Animation UX Refactor.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
HoverPreset,
|
|
14
|
+
CSSHoverPreset,
|
|
15
|
+
ShaderHoverPreset,
|
|
16
|
+
HoverEasing,
|
|
17
|
+
HoverEffectConfig,
|
|
18
|
+
ShaderSmoothness,
|
|
19
|
+
} from "./hover-effect-types";
|
|
20
|
+
import { HOVER_EFFECT_DEFAULTS } from "./hover-effect-types";
|
|
21
|
+
|
|
22
|
+
// ── Default config ─────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_HOVER_EFFECT: Required<HoverEffectConfig> = {
|
|
25
|
+
preset: HOVER_EFFECT_DEFAULTS.preset,
|
|
26
|
+
duration: HOVER_EFFECT_DEFAULTS.duration,
|
|
27
|
+
easing: HOVER_EFFECT_DEFAULTS.easing,
|
|
28
|
+
shader_speed: HOVER_EFFECT_DEFAULTS.shader_speed,
|
|
29
|
+
shader_smoothness: HOVER_EFFECT_DEFAULTS.shader_smoothness,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// ── Preset metadata (for builder UI) ───────────────────────────────
|
|
33
|
+
|
|
34
|
+
export interface HoverPresetInfo {
|
|
35
|
+
id: HoverPreset;
|
|
36
|
+
label: string;
|
|
37
|
+
description: string;
|
|
38
|
+
/** Whether this preset uses WebGL shaders */
|
|
39
|
+
isShader: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const HOVER_PRESET_INFO: HoverPresetInfo[] = [
|
|
43
|
+
// CSS-based presets
|
|
44
|
+
{ id: "none", label: "None", description: "No hover effect", isShader: false },
|
|
45
|
+
{ id: "scale-up", label: "Scale Up", description: "Zoom in on hover", isShader: false },
|
|
46
|
+
{ id: "scale-down", label: "Scale Down", description: "Zoom out on hover", isShader: false },
|
|
47
|
+
{ id: "lift", label: "Lift", description: "Lifts element with shadow", isShader: false },
|
|
48
|
+
{ id: "tilt-3d", label: "Tilt 3D", description: "Perspective tilt following cursor", isShader: false },
|
|
49
|
+
{ id: "color-shift", label: "Color Shift", description: "Brightness and saturation change", isShader: false },
|
|
50
|
+
{ id: "blur-reveal", label: "Blur Reveal", description: "Blurred until hovered", isShader: false },
|
|
51
|
+
{ id: "border-glow", label: "Border Glow", description: "Glowing border on hover", isShader: false },
|
|
52
|
+
// Shader-based presets
|
|
53
|
+
{ id: "ripple", label: "Ripple", description: "Ripple distortion from cursor", isShader: true },
|
|
54
|
+
{ id: "rgb-shift", label: "RGB Shift", description: "Chromatic aberration on hover", isShader: true },
|
|
55
|
+
{ id: "pixelate", label: "Pixelate", description: "Pixelation dissolve effect", isShader: true },
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
/** Quick lookup: preset ID → metadata */
|
|
59
|
+
export const HOVER_PRESET_MAP: Record<HoverPreset, HoverPresetInfo> = Object.fromEntries(
|
|
60
|
+
HOVER_PRESET_INFO.map((p) => [p.id, p])
|
|
61
|
+
) as Record<HoverPreset, HoverPresetInfo>;
|
|
62
|
+
|
|
63
|
+
// ── Shader detection ───────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
const SHADER_PRESETS: ReadonlySet<string> = new Set<ShaderHoverPreset>([
|
|
66
|
+
"ripple",
|
|
67
|
+
"rgb-shift",
|
|
68
|
+
"pixelate",
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if a hover preset uses WebGL shaders.
|
|
73
|
+
* Used by the builder to show/hide shader-specific controls
|
|
74
|
+
* and by the renderer to delegate to ShaderCanvas.
|
|
75
|
+
*/
|
|
76
|
+
export function isShaderPreset(preset: HoverPreset): preset is ShaderHoverPreset {
|
|
77
|
+
return SHADER_PRESETS.has(preset);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── Easing options (for builder UI) ────────────────────────────────
|
|
81
|
+
|
|
82
|
+
export const HOVER_EFFECT_EASINGS: { id: HoverEasing; label: string }[] = [
|
|
83
|
+
{ id: "ease-out", label: "Ease Out" },
|
|
84
|
+
{ id: "ease", label: "Ease" },
|
|
85
|
+
{ id: "ease-in", label: "Ease In" },
|
|
86
|
+
{ id: "ease-in-out", label: "Ease In Out" },
|
|
87
|
+
{ id: "linear", label: "Linear" },
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
// ── Duration range (for builder UI) ────────────────────────────────
|
|
91
|
+
|
|
92
|
+
export const HOVER_DURATION_RANGE = { min: 50, max: 5000, step: 25, default: 300 } as const;
|
|
93
|
+
|
|
94
|
+
// ── Shader speed / smoothness ranges (for builder UI) ──────────────
|
|
95
|
+
|
|
96
|
+
export const SHADER_SPEED_RANGE = { min: 0.5, max: 3.0, step: 0.1, default: 1.0 } as const;
|
|
97
|
+
|
|
98
|
+
export const SHADER_SMOOTHNESS_OPTIONS: { id: ShaderSmoothness; label: string }[] = [
|
|
99
|
+
{ id: "snappy", label: "Snappy" },
|
|
100
|
+
{ id: "normal", label: "Normal" },
|
|
101
|
+
{ id: "smooth", label: "Smooth" },
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
/** Smoothness label → lerp factor (used by ShaderCanvas renderer) */
|
|
105
|
+
export const SMOOTHNESS_VALUES: Record<ShaderSmoothness, number> = {
|
|
106
|
+
snappy: 0.15,
|
|
107
|
+
normal: 0.05,
|
|
108
|
+
smooth: 0.02,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// ── CSS hover styles ───────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
export interface HoverStyles {
|
|
114
|
+
/** Base state CSS (always applied) */
|
|
115
|
+
base: Record<string, string>;
|
|
116
|
+
/** Hover state CSS (applied on :hover) */
|
|
117
|
+
hover: Record<string, string>;
|
|
118
|
+
/** Whether this preset needs JS mouse tracking (tilt-3d) */
|
|
119
|
+
needsJS?: boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Returns CSS properties for a CSS-based hover preset.
|
|
124
|
+
* Returns null for "none" and shader presets (they use WebGL, not CSS).
|
|
125
|
+
*
|
|
126
|
+
* Intensity is removed in the new system — presets use fixed,
|
|
127
|
+
* sensible values. The old intensity multiplier created confusion
|
|
128
|
+
* without meaningful user value.
|
|
129
|
+
*/
|
|
130
|
+
export function getHoverEffectStyles(
|
|
131
|
+
preset: HoverPreset,
|
|
132
|
+
duration: number = 300,
|
|
133
|
+
easing: string = "ease-out",
|
|
134
|
+
): HoverStyles | null {
|
|
135
|
+
// Shader presets don't use CSS
|
|
136
|
+
if (isShaderPreset(preset)) return null;
|
|
137
|
+
|
|
138
|
+
const cssPreset = preset as CSSHoverPreset;
|
|
139
|
+
const transition = `all ${duration}ms ${easing}`;
|
|
140
|
+
|
|
141
|
+
switch (cssPreset) {
|
|
142
|
+
case "none":
|
|
143
|
+
return null;
|
|
144
|
+
|
|
145
|
+
case "scale-up":
|
|
146
|
+
return {
|
|
147
|
+
base: { transition, transform: "scale(1)" },
|
|
148
|
+
hover: { transform: "scale(1.05)" },
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
case "scale-down":
|
|
152
|
+
return {
|
|
153
|
+
base: { transition, transform: "scale(1)" },
|
|
154
|
+
hover: { transform: "scale(0.95)" },
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
case "lift":
|
|
158
|
+
return {
|
|
159
|
+
base: {
|
|
160
|
+
transition,
|
|
161
|
+
transform: "translateY(0)",
|
|
162
|
+
boxShadow: "0 0 0 rgba(0,0,0,0)",
|
|
163
|
+
},
|
|
164
|
+
hover: {
|
|
165
|
+
transform: "translateY(-4px)",
|
|
166
|
+
boxShadow: "0 8px 20px rgba(0,0,0,0.15)",
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
case "tilt-3d":
|
|
171
|
+
return {
|
|
172
|
+
base: {
|
|
173
|
+
transition,
|
|
174
|
+
transform: "perspective(800px) rotateX(0deg) rotateY(0deg)",
|
|
175
|
+
transformStyle: "preserve-3d",
|
|
176
|
+
},
|
|
177
|
+
hover: {
|
|
178
|
+
// Overridden by JS mousemove handler
|
|
179
|
+
transform: "perspective(800px) rotateX(0deg) rotateY(0deg)",
|
|
180
|
+
},
|
|
181
|
+
needsJS: true,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
case "color-shift":
|
|
185
|
+
return {
|
|
186
|
+
base: { transition, filter: "brightness(1) saturate(1)" },
|
|
187
|
+
hover: { filter: "brightness(1.10) saturate(1.20)" },
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
case "blur-reveal":
|
|
191
|
+
return {
|
|
192
|
+
base: { transition, filter: "blur(3px)", opacity: "0.85" },
|
|
193
|
+
hover: { filter: "blur(0px)", opacity: "1" },
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
case "border-glow":
|
|
197
|
+
return {
|
|
198
|
+
base: {
|
|
199
|
+
transition,
|
|
200
|
+
boxShadow: "0 0 0 0 rgba(7,107,255,0)",
|
|
201
|
+
},
|
|
202
|
+
hover: {
|
|
203
|
+
boxShadow: "0 0 8px 4px rgba(7,107,255,0.40)",
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
default:
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|