feat: anchor links, smooth scroll, back-to-top
This commit is contained in:
parent
a018a65db9
commit
21e3778397
260
add-anchor-links.sh
Normal file
260
add-anchor-links.sh
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# add-anchor-links.sh (macOS + Linux compatible)
|
||||||
|
# Run from your project root: bash add-anchor-links.sh
|
||||||
|
# ============================================================================
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🔗 Adding anchor link support to all FD blocks..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Step 1: Create shared anchorField ──────────────────────────────────────
|
||||||
|
|
||||||
|
mkdir -p src/fields
|
||||||
|
|
||||||
|
cat > src/fields/anchorField.ts << 'FIELDEOF'
|
||||||
|
import type { Field } from 'payload'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared anchorId field for all FD blocks.
|
||||||
|
* Allows editors to set an anchor link ID on any section,
|
||||||
|
* enabling direct linking like /page#section-name
|
||||||
|
*
|
||||||
|
* Usage in block config:
|
||||||
|
* import { anchorField } from '@/fields/anchorField'
|
||||||
|
* fields: [ ...contentFields, anchorField ]
|
||||||
|
*
|
||||||
|
* Usage in block component:
|
||||||
|
* <section id={anchorId || undefined} ...>
|
||||||
|
*/
|
||||||
|
export const anchorField: Field = {
|
||||||
|
name: 'anchorId',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Ankarlänk-ID',
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
'Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.',
|
||||||
|
position: 'sidebar',
|
||||||
|
},
|
||||||
|
validate: (value: unknown) => {
|
||||||
|
if (!value) return true
|
||||||
|
if (typeof value === 'string' && /^[a-z0-9][a-z0-9-]*$/.test(value)) return true
|
||||||
|
return 'Använd bara små bokstäver (a-z), siffror (0-9) och bindestreck (-). Inga mellanslag.'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
FIELDEOF
|
||||||
|
|
||||||
|
echo "✅ Created src/fields/anchorField.ts"
|
||||||
|
|
||||||
|
# ── Step 2: Add anchorField to all block configs ───────────────────────────
|
||||||
|
|
||||||
|
BLOCKS=(
|
||||||
|
FDHeroBlock
|
||||||
|
FDAlternateHeroBlock
|
||||||
|
FDTextBlock
|
||||||
|
FDPricingCardBlock
|
||||||
|
FDFaqBlock
|
||||||
|
FDContactBlock
|
||||||
|
FDContactFormBlock
|
||||||
|
FDDataTableBlock
|
||||||
|
FDCardGridBlock
|
||||||
|
FDCtaSideImageBlock
|
||||||
|
FDCtaBannerBlock
|
||||||
|
FDCodeEmbedBlock
|
||||||
|
FDFeatureAnnouncementBlock
|
||||||
|
FDHeaderTextImageBlock
|
||||||
|
FDServicesGridBlock
|
||||||
|
FDServiceChooserBlock
|
||||||
|
FDUspChecklistBlock
|
||||||
|
FDUspTableBlock
|
||||||
|
FDTechPropertiesBlock
|
||||||
|
FDStatisticsBlock
|
||||||
|
FDTestimonialBlock
|
||||||
|
FDTeamBlock
|
||||||
|
FDVideoBlock
|
||||||
|
FDVpsCalculatorBlock
|
||||||
|
FDWideCardBlock
|
||||||
|
FDServiceCalculatorBlock
|
||||||
|
)
|
||||||
|
|
||||||
|
for BLOCK in "${BLOCKS[@]}"; do
|
||||||
|
CONFIG="src/blocks/$BLOCK/config.ts"
|
||||||
|
if [ ! -f "$CONFIG" ]; then
|
||||||
|
echo "⚠️ Skipped $BLOCK — config not found"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Skip if already has anchorField
|
||||||
|
if grep -q "anchorField" "$CONFIG"; then
|
||||||
|
echo "⏭️ Skipped $BLOCK — anchorField already present"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add import after the first line
|
||||||
|
perl -i -pe 'if ($. == 1) { $_ .= "import { anchorField } from '\''\@/fields/anchorField'\''\n" }' "$CONFIG"
|
||||||
|
|
||||||
|
# Add anchorField before the last "]," in the file
|
||||||
|
perl -i -0pe 's/( \],\n\})\s*$/ anchorField,\n$1/s' "$CONFIG"
|
||||||
|
|
||||||
|
echo "✅ Updated $CONFIG"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Step 3: Add anchorId to all block components ──────────────────────────
|
||||||
|
|
||||||
|
echo "🔧 Updating block components..."
|
||||||
|
|
||||||
|
for BLOCK in "${BLOCKS[@]}"; do
|
||||||
|
COMP="src/blocks/$BLOCK/Component.tsx"
|
||||||
|
if [ ! -f "$COMP" ]; then
|
||||||
|
echo "⚠️ Skipped $BLOCK component — not found"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Skip if already has anchorId
|
||||||
|
if grep -q "anchorId" "$COMP"; then
|
||||||
|
echo "⏭️ Skipped $BLOCK component — anchorId already present"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add anchorId to destructured props: insert before "}) => {"
|
||||||
|
perl -i -pe 's/\}\) => \{/ anchorId,\n}) => {/' "$COMP"
|
||||||
|
|
||||||
|
# Add id={anchorId || undefined} to the first <section
|
||||||
|
# Handle single-line: <section className=...
|
||||||
|
perl -i -pe 'if (!$done && s/<section className/<section id={anchorId || undefined} className/) { $done=1 }' "$COMP"
|
||||||
|
|
||||||
|
# Handle multi-line: <section alone on a line (like FDHeroBlock)
|
||||||
|
if ! grep -q 'id={anchorId' "$COMP"; then
|
||||||
|
perl -i -pe 'if (!$done && /^\s+<section\s*$/) { $done=1; s|<section|<section id={anchorId \|\| undefined}| }' "$COMP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Updated $COMP"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Step 4: Add smooth scrolling to globals.css ───────────────────────────
|
||||||
|
|
||||||
|
GLOBALS_CSS="src/app/(frontend)/globals.css"
|
||||||
|
|
||||||
|
if grep -q "scroll-behavior" "$GLOBALS_CSS" 2>/dev/null; then
|
||||||
|
echo "⏭️ Smooth scroll already in globals.css"
|
||||||
|
else
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
cat > "$TMPFILE" << 'CSSEOF'
|
||||||
|
/* Smooth scroll + header offset for anchor links */
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
scroll-padding-top: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSSEOF
|
||||||
|
cat "$GLOBALS_CSS" >> "$TMPFILE"
|
||||||
|
mv "$TMPFILE" "$GLOBALS_CSS"
|
||||||
|
echo "✅ Added smooth scroll to globals.css"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Step 5: Create BackToTop component ────────────────────────────────────
|
||||||
|
|
||||||
|
mkdir -p src/components/BackToTop
|
||||||
|
|
||||||
|
cat > src/components/BackToTop/index.tsx << 'BTEOF'
|
||||||
|
'use client'
|
||||||
|
import React, { useState, useEffect, useCallback } from 'react'
|
||||||
|
import { ChevronUpIcon } from 'lucide-react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Floating "back to top" button.
|
||||||
|
* Appears after scrolling down 400px, smooth-scrolls to top on click.
|
||||||
|
* Styled to match FD design system.
|
||||||
|
*/
|
||||||
|
export const BackToTop: React.FC = () => {
|
||||||
|
const [visible, setVisible] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onScroll = () => {
|
||||||
|
setVisible(window.scrollY > 400)
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', onScroll, { passive: true })
|
||||||
|
return () => window.removeEventListener('scroll', onScroll)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const scrollToTop = useCallback(() => {
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={scrollToTop}
|
||||||
|
aria-label="Tillbaka till toppen"
|
||||||
|
className={`
|
||||||
|
fixed bottom-6 right-6 z-40
|
||||||
|
w-11 h-11 rounded-full
|
||||||
|
bg-fd-navy/80 backdrop-blur-sm
|
||||||
|
text-white hover:text-fd-yellow
|
||||||
|
border border-white/10 hover:border-fd-yellow/30
|
||||||
|
shadow-lg hover:shadow-xl
|
||||||
|
flex items-center justify-center
|
||||||
|
transition-all duration-300 ease-in-out cursor-pointer
|
||||||
|
${visible
|
||||||
|
? 'opacity-100 translate-y-0 pointer-events-auto'
|
||||||
|
: 'opacity-0 translate-y-4 pointer-events-none'
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<ChevronUpIcon className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BTEOF
|
||||||
|
|
||||||
|
echo "✅ Created src/components/BackToTop/index.tsx"
|
||||||
|
|
||||||
|
# ── Step 6: Add BackToTop to layout ───────────────────────────────────────
|
||||||
|
|
||||||
|
LAYOUT="src/app/(frontend)/layout.tsx"
|
||||||
|
|
||||||
|
if grep -q "BackToTop" "$LAYOUT" 2>/dev/null; then
|
||||||
|
echo "⏭️ BackToTop already in layout"
|
||||||
|
else
|
||||||
|
# Add import after Footer import
|
||||||
|
perl -i -pe 'if (/import \{ Footer \}/) { $_ .= "import { BackToTop } from '\''\@/components/BackToTop'\''\n" }' "$LAYOUT"
|
||||||
|
|
||||||
|
# Add component after <Footer />
|
||||||
|
perl -i -pe 's|<Footer />|<Footer />\n <BackToTop />|' "$LAYOUT"
|
||||||
|
|
||||||
|
echo "✅ Added BackToTop to layout.tsx"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════════════"
|
||||||
|
echo "✅ All done! Now follow the deployment steps below."
|
||||||
|
echo "════════════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
echo "LOCAL:"
|
||||||
|
echo " 1. Review changes: git diff"
|
||||||
|
echo " 2. Clean local migrations:"
|
||||||
|
echo " rm -f src/migrations/2025*.ts src/migrations/2026*.ts"
|
||||||
|
echo " echo 'export {}' > src/migrations/index.ts"
|
||||||
|
echo " 3. Commit and push:"
|
||||||
|
echo " git add -A"
|
||||||
|
echo " git commit -m 'feat: anchor links, smooth scroll, back-to-top'"
|
||||||
|
echo " git push origin main"
|
||||||
|
echo ""
|
||||||
|
echo "SERVER:"
|
||||||
|
echo " cd /var/www/fiberdirekt"
|
||||||
|
echo " git pull origin main"
|
||||||
|
echo " npm install"
|
||||||
|
echo " npx payload generate:types"
|
||||||
|
echo " npx payload migrate:create add-anchor-links"
|
||||||
|
echo " npx payload migrate"
|
||||||
|
echo " npm run build && pm2 restart fiberdirekt"
|
||||||
|
echo ""
|
||||||
|
echo "If migrate:create says 'no changes detected', skip migrate"
|
||||||
|
echo "steps and go straight to build. Block fields are stored as"
|
||||||
|
echo "JSON so new optional fields usually don't need SQL migration."
|
||||||
|
echo ""
|
||||||
@ -1,3 +1,9 @@
|
|||||||
|
/* Smooth scroll + header offset for anchor links */
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
scroll-padding-top: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
@import 'tw-animate-css';
|
@import 'tw-animate-css';
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { GeistSans } from 'geist/font/sans'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { AdminBar } from '@/components/AdminBar'
|
import { AdminBar } from '@/components/AdminBar'
|
||||||
import { Footer } from '@/Footer/Component'
|
import { Footer } from '@/Footer/Component'
|
||||||
|
import { BackToTop } from '@/components/BackToTop'
|
||||||
import { Header } from '@/Header/Component'
|
import { Header } from '@/Header/Component'
|
||||||
import { Providers } from '@/providers'
|
import { Providers } from '@/providers'
|
||||||
import { InitTheme } from '@/providers/Theme/InitTheme'
|
import { InitTheme } from '@/providers/Theme/InitTheme'
|
||||||
@ -67,6 +68,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
|||||||
<Header />
|
<Header />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
|
<BackToTop />
|
||||||
<PopupAnnouncementComponent {...popupData} />
|
<PopupAnnouncementComponent {...popupData} />
|
||||||
{cookieEnabled && (
|
{cookieEnabled && (
|
||||||
<CookieConsent
|
<CookieConsent
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export const FDAlternateHeroBlockComponent: React.FC<Props> = ({
|
|||||||
image,
|
image,
|
||||||
imageCaption,
|
imageCaption,
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const media = image as Media | undefined
|
const media = image as Media | undefined
|
||||||
const hasImage = media && typeof media === 'object' && media.url
|
const hasImage = media && typeof media === 'object' && media.url
|
||||||
@ -34,7 +35,7 @@ export const FDAlternateHeroBlockComponent: React.FC<Props> = ({
|
|||||||
: 'text-fd-navy dark:text-white'
|
: 'text-fd-navy dark:text-white'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full pt-16 md:pt-20 lg:pt-[99px] ${bgClass}`}>
|
<section id={anchorId || undefined} className={`w-full pt-16 md:pt-20 lg:pt-[99px] ${bgClass}`}>
|
||||||
{/* Centered content */}
|
{/* Centered content */}
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 text-center flex flex-col items-center gap-6 pb-12 md:pb-16">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 text-center flex flex-col items-center gap-6 pb-12 md:pb-16">
|
||||||
<h1 className={`w-full max-w-[820px] font-joey-heavy text-fd-display ${titleClass}`}>
|
<h1 className={`w-full max-w-[820px] font-joey-heavy text-fd-display ${titleClass}`}>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDAlternateHeroBlock: Block = {
|
export const FDAlternateHeroBlock: Block = {
|
||||||
slug: 'fdAlternateHero',
|
slug: 'fdAlternateHero',
|
||||||
@ -78,5 +79,6 @@ export const FDAlternateHeroBlock: Block = {
|
|||||||
{ label: 'Grå', value: 'gray' },
|
{ label: 'Grå', value: 'gray' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -71,13 +71,14 @@ export const FDCardGridBlockComponent: React.FC<FDCardGridBlockProps> = ({
|
|||||||
cardStyle = 'outlined',
|
cardStyle = 'outlined',
|
||||||
cards,
|
cards,
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const style = cardStyleMap[cardStyle] || cardStyleMap.outlined
|
const style = cardStyleMap[cardStyle] || cardStyleMap.outlined
|
||||||
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
||||||
const gridCols = layoutGridMap[layout] || layoutGridMap['1-1-1']
|
const gridCols = layoutGridMap[layout] || layoutGridMap['1-1-1']
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
<div className={`grid grid-cols-1 md:grid-cols-2 ${gridCols} gap-4 md:gap-6`}>
|
<div className={`grid grid-cols-1 md:grid-cols-2 ${gridCols} gap-4 md:gap-6`}>
|
||||||
{cards?.map((card, index) => {
|
{cards?.map((card, index) => {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDCardGridBlock: Block = {
|
export const FDCardGridBlock: Block = {
|
||||||
slug: 'fdCardGrid',
|
slug: 'fdCardGrid',
|
||||||
@ -134,5 +135,6 @@ export const FDCardGridBlock: Block = {
|
|||||||
{ label: 'Grå', value: 'gray' },
|
{ label: 'Grå', value: 'gray' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -36,6 +36,7 @@ export const FDCodeEmbedBlockComponent: React.FC<FDCodeEmbedBlockProps> = ({
|
|||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
textColor = 'auto',
|
textColor = 'auto',
|
||||||
embedBackground = 'none',
|
embedBackground = 'none',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const customCodeRef = useRef<HTMLDivElement>(null)
|
const customCodeRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ export const FDCodeEmbedBlockComponent: React.FC<FDCodeEmbedBlockProps> = ({
|
|||||||
: ''
|
: ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`relative w-full py-16 md:py-20 lg:py-[99px] ${bgClass} overflow-hidden`}>
|
<section id={anchorId || undefined} className={`relative w-full py-16 md:py-20 lg:py-[99px] ${bgClass} overflow-hidden`}>
|
||||||
<div className={`relative ${containerClass} mx-auto px-6 md:px-8`}>
|
<div className={`relative ${containerClass} mx-auto px-6 md:px-8`}>
|
||||||
{(heading || description) && (
|
{(heading || description) && (
|
||||||
<div className="mb-8 md:mb-12 text-center">
|
<div className="mb-8 md:mb-12 text-center">
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDCodeEmbedBlock: Block = {
|
export const FDCodeEmbedBlock: Block = {
|
||||||
slug: 'fdCodeEmbed',
|
slug: 'fdCodeEmbed',
|
||||||
@ -151,5 +152,6 @@ export const FDCodeEmbedBlock: Block = {
|
|||||||
{ label: 'Navy kort', value: 'navy-card' },
|
{ label: 'Navy kort', value: 'navy-card' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -8,9 +8,10 @@ const imageRadius = 'rounded-[16px] md:rounded-[24px] lg:rounded-[30px]'
|
|||||||
export const FDContactBlockComponent: React.FC<FDContactBlockProps> = ({
|
export const FDContactBlockComponent: React.FC<FDContactBlockProps> = ({
|
||||||
heading,
|
heading,
|
||||||
contactMethods,
|
contactMethods,
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<section className="relative w-full bg-fd-navy py-16 md:py-20 lg:pt-[100px] lg:pb-[120px]">
|
<section id={anchorId || undefined} className="relative w-full bg-fd-navy py-16 md:py-20 lg:pt-[100px] lg:pb-[120px]">
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-center gap-8 lg:gap-10">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-center gap-8 lg:gap-10">
|
||||||
<h2 className="w-full font-joey-heavy text-fd-h1 text-fd-yellow text-center">
|
<h2 className="w-full font-joey-heavy text-fd-h1 text-fd-yellow text-center">
|
||||||
{heading}
|
{heading}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDContactBlock: Block = {
|
export const FDContactBlock: Block = {
|
||||||
slug: 'fdContact',
|
slug: 'fdContact',
|
||||||
@ -48,5 +49,6 @@ export const FDContactBlock: Block = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -37,6 +37,7 @@ export const FDContactFormBlockComponent: React.FC<FDContactFormBlockProps> = ({
|
|||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
layout = 'standard',
|
layout = 'standard',
|
||||||
externalApi,
|
externalApi,
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const [formData, setFormData] = useState<Record<string, string>>({})
|
const [formData, setFormData] = useState<Record<string, string>>({})
|
||||||
const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle')
|
const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle')
|
||||||
@ -278,7 +279,7 @@ export const FDContactFormBlockComponent: React.FC<FDContactFormBlockProps> = ({
|
|||||||
|
|
||||||
if (status === 'sent') {
|
if (status === 'sent') {
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 text-center">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 text-center">
|
||||||
<div className={`inline-flex items-center justify-center w-16 h-16 rounded-full mb-6 ${dark ? 'bg-fd-yellow/20' : 'bg-fd-mint/20 dark:bg-fd-yellow/20'}`}>
|
<div className={`inline-flex items-center justify-center w-16 h-16 rounded-full mb-6 ${dark ? 'bg-fd-yellow/20' : 'bg-fd-mint/20 dark:bg-fd-yellow/20'}`}>
|
||||||
<svg className={`w-8 h-8 ${dark ? 'text-fd-yellow' : 'text-fd-mint dark:text-fd-yellow'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
<svg className={`w-8 h-8 ${dark ? 'text-fd-yellow' : 'text-fd-mint dark:text-fd-yellow'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDContactFormBlock: Block = {
|
export const FDContactFormBlock: Block = {
|
||||||
slug: 'fdContactForm',
|
slug: 'fdContactForm',
|
||||||
@ -153,5 +154,6 @@ export const FDContactFormBlock: Block = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -51,13 +51,14 @@ export const FDCtaBannerBlockComponent: React.FC<FDCtaBannerBlockProps> = ({
|
|||||||
sectionBackground = 'yellow',
|
sectionBackground = 'yellow',
|
||||||
alignment = 'center',
|
alignment = 'center',
|
||||||
size = 'medium',
|
size = 'medium',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = bgMap[sectionBackground ?? 'yellow'] || bgMap.yellow
|
const theme = bgMap[sectionBackground ?? 'yellow'] || bgMap.yellow
|
||||||
const sizing = sizeMap[size ?? 'medium'] || sizeMap.medium
|
const sizing = sizeMap[size ?? 'medium'] || sizeMap.medium
|
||||||
const isCenter = alignment === 'center'
|
const isCenter = alignment === 'center'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full ${sizing.py} ${theme.section}`}>
|
<section id={anchorId || undefined} className={`w-full ${sizing.py} ${theme.section}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
<div className={`flex flex-col gap-6 md:gap-8 ${isCenter ? 'items-center text-center' : 'items-start text-left'} max-w-[800px] ${isCenter ? 'mx-auto' : ''}`}>
|
<div className={`flex flex-col gap-6 md:gap-8 ${isCenter ? 'items-center text-center' : 'items-start text-left'} max-w-[800px] ${isCenter ? 'mx-auto' : ''}`}>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDCtaBannerBlock: Block = {
|
export const FDCtaBannerBlock: Block = {
|
||||||
slug: 'fdCtaBanner',
|
slug: 'fdCtaBanner',
|
||||||
@ -84,5 +85,6 @@ export const FDCtaBannerBlock: Block = {
|
|||||||
{ label: 'Stor', value: 'large' },
|
{ label: 'Stor', value: 'large' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -28,6 +28,7 @@ export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> =
|
|||||||
imageOverlay = 'none',
|
imageOverlay = 'none',
|
||||||
imagePosition = 'right',
|
imagePosition = 'right',
|
||||||
theme = 'dark',
|
theme = 'dark',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const isDark = theme === 'dark'
|
const isDark = theme === 'dark'
|
||||||
const hasImage = !!image
|
const hasImage = !!image
|
||||||
@ -79,7 +80,7 @@ export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> =
|
|||||||
) : null
|
) : null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col lg:flex-row items-center gap-10 lg:gap-16">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col lg:flex-row items-center gap-10 lg:gap-16">
|
||||||
{imagePosition === 'left' ? (
|
{imagePosition === 'left' ? (
|
||||||
<>{imageContent}{textContent}</>
|
<>{imageContent}{textContent}</>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDCtaSideImageBlock: Block = {
|
export const FDCtaSideImageBlock: Block = {
|
||||||
slug: 'fdCtaSideImage',
|
slug: 'fdCtaSideImage',
|
||||||
@ -83,5 +84,6 @@ export const FDCtaSideImageBlock: Block = {
|
|||||||
{ label: 'Mörkt', value: 'dark' },
|
{ label: 'Mörkt', value: 'dark' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -83,6 +83,7 @@ export const FDDataTableBlockComponent: React.FC<Props> = ({
|
|||||||
stripeRows = true,
|
stripeRows = true,
|
||||||
bordered = false,
|
bordered = false,
|
||||||
firstColumnBold = false,
|
firstColumnBold = false,
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const [tableData, setTableData] = useState<TableData | null>(null)
|
const [tableData, setTableData] = useState<TableData | null>(null)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@ -166,7 +167,7 @@ export const FDDataTableBlockComponent: React.FC<Props> = ({
|
|||||||
: ''
|
: ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
|
|
||||||
{(heading || description) && (
|
{(heading || description) && (
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDDataTableBlock: Block = {
|
export const FDDataTableBlock: Block = {
|
||||||
slug: 'fdDataTable',
|
slug: 'fdDataTable',
|
||||||
@ -143,5 +144,6 @@ export const FDDataTableBlock: Block = {
|
|||||||
description: 'Gör den första kolumnen fet — användbart för namnkolumner.',
|
description: 'Gör den första kolumnen fet — användbart för namnkolumner.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@ export const FDFaqBlockComponent: React.FC<FDFaqBlockProps> = ({
|
|||||||
heading,
|
heading,
|
||||||
items,
|
items,
|
||||||
theme = 'gray',
|
theme = 'gray',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const [openIndex, setOpenIndex] = useState<number | null>(null)
|
const [openIndex, setOpenIndex] = useState<number | null>(null)
|
||||||
/* Generate a stable unique prefix for this block instance */
|
/* Generate a stable unique prefix for this block instance */
|
||||||
@ -37,7 +38,7 @@ export const FDFaqBlockComponent: React.FC<FDFaqBlockProps> = ({
|
|||||||
: 'text-fd-navy/80 dark:text-white/80'
|
: 'text-fd-navy/80 dark:text-white/80'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[130px] ${bgClass}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[130px] ${bgClass}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-start gap-6">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-start gap-6">
|
||||||
<h2 className={`w-full max-w-[550px] font-joey-heavy text-fd-h1 ${headingColor}`}>
|
<h2 className={`w-full max-w-[550px] font-joey-heavy text-fd-h1 ${headingColor}`}>
|
||||||
{heading}
|
{heading}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDFaqBlock: Block = {
|
export const FDFaqBlock: Block = {
|
||||||
slug: 'fdFaq',
|
slug: 'fdFaq',
|
||||||
@ -50,5 +51,6 @@ export const FDFaqBlock: Block = {
|
|||||||
{ label: 'Mörkt', value: 'dark' },
|
{ label: 'Mörkt', value: 'dark' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -8,6 +8,7 @@ export const FDFeatureAnnouncementBlockComponent: React.FC<FDFeatureAnnouncement
|
|||||||
ctaText,
|
ctaText,
|
||||||
ctaLink = '#',
|
ctaLink = '#',
|
||||||
theme = 'gray',
|
theme = 'gray',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const isDark = theme === 'dark'
|
const isDark = theme === 'dark'
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ export const FDFeatureAnnouncementBlockComponent: React.FC<FDFeatureAnnouncement
|
|||||||
const onDark = isDark || true // once dark: kicks in bg is navy anyway
|
const onDark = isDark || true // once dark: kicks in bg is navy anyway
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-20 md:py-28 lg:py-[173px] ${bgClass}`}>
|
<section id={anchorId || undefined} className={`w-full py-20 md:py-28 lg:py-[173px] ${bgClass}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-center gap-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-center gap-8">
|
||||||
<h2
|
<h2
|
||||||
className={`w-full max-w-[696px] font-joey-bold text-fd-h1 text-center leading-tight ${headingColor}`}
|
className={`w-full max-w-[696px] font-joey-bold text-fd-h1 text-center leading-tight ${headingColor}`}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDFeatureAnnouncementBlock: Block = {
|
export const FDFeatureAnnouncementBlock: Block = {
|
||||||
slug: 'fdFeatureAnnouncement',
|
slug: 'fdFeatureAnnouncement',
|
||||||
@ -47,5 +48,6 @@ export const FDFeatureAnnouncementBlock: Block = {
|
|||||||
{ label: 'Mörkt', value: 'dark' },
|
{ label: 'Mörkt', value: 'dark' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -47,6 +47,7 @@ export const FDHeaderTextImageBlockComponent: React.FC<FDHeaderTextImageBlockPro
|
|||||||
textAlign = 'center',
|
textAlign = 'center',
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
textColor = 'navy',
|
textColor = 'navy',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const bg = bgMap[sectionBackground || 'white']
|
const bg = bgMap[sectionBackground || 'white']
|
||||||
const headClr = headingMap[textColor || 'navy']
|
const headClr = headingMap[textColor || 'navy']
|
||||||
@ -57,7 +58,7 @@ export const FDHeaderTextImageBlockComponent: React.FC<FDHeaderTextImageBlockPro
|
|||||||
const media = image as Media
|
const media = image as Media
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col gap-8 md:gap-10">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col gap-8 md:gap-10">
|
||||||
{(heading || body) && (
|
{(heading || body) && (
|
||||||
<div className={`flex flex-col gap-4 md:gap-6 ${align} ${textAlign === 'center' ? 'max-w-[900px] mx-auto' : ''}`}>
|
<div className={`flex flex-col gap-4 md:gap-6 ${align} ${textAlign === 'center' ? 'max-w-[900px] mx-auto' : ''}`}>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDHeaderTextImageBlock: Block = {
|
export const FDHeaderTextImageBlock: Block = {
|
||||||
slug: 'fdHeaderTextImage',
|
slug: 'fdHeaderTextImage',
|
||||||
@ -87,5 +88,6 @@ export const FDHeaderTextImageBlock: Block = {
|
|||||||
{ label: 'Vit', value: 'white' },
|
{ label: 'Vit', value: 'white' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -15,6 +15,7 @@ export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
|||||||
overlayOpacity = '50',
|
overlayOpacity = '50',
|
||||||
textColor = 'auto',
|
textColor = 'auto',
|
||||||
theme = 'light',
|
theme = 'light',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const media = backgroundImage as Media | undefined
|
const media = backgroundImage as Media | undefined
|
||||||
const hasBgImage = Boolean(media?.url)
|
const hasBgImage = Boolean(media?.url)
|
||||||
@ -39,7 +40,7 @@ export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
|||||||
const secondaryOnDark = textColor === 'navy' ? false : isDark
|
const secondaryOnDark = textColor === 'navy' ? false : isDark
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section id={anchorId || undefined}
|
||||||
className={`relative w-full py-16 md:py-20 lg:py-[99px] ${
|
className={`relative w-full py-16 md:py-20 lg:py-[99px] ${
|
||||||
hasBgImage ? '' : isDark ? 'bg-fd-navy' : 'bg-white dark:bg-fd-navy'
|
hasBgImage ? '' : isDark ? 'bg-fd-navy' : 'bg-white dark:bg-fd-navy'
|
||||||
} overflow-hidden`}
|
} overflow-hidden`}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDHeroBlock: Block = {
|
export const FDHeroBlock: Block = {
|
||||||
slug: 'fdHero',
|
slug: 'fdHero',
|
||||||
@ -109,5 +110,6 @@ export const FDHeroBlock: Block = {
|
|||||||
{ label: 'Mörkt', value: 'dark' },
|
{ label: 'Mörkt', value: 'dark' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -78,6 +78,7 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
|
|||||||
buttonColor = 'yellow',
|
buttonColor = 'yellow',
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
titleColor = 'navy',
|
titleColor = 'navy',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
||||||
const sectionTitleColor = titleColorMap[titleColor || 'navy']
|
const sectionTitleColor = titleColorMap[titleColor || 'navy']
|
||||||
@ -88,7 +89,7 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
|
|||||||
const outlineOnDark = style.isDark
|
const outlineOnDark = style.isDark
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
{sectionTitle && (
|
{sectionTitle && (
|
||||||
<h2 className={`font-joey-heavy text-fd-h1 text-center mb-10 md:mb-14 ${sectionTitleColor}`}>
|
<h2 className={`font-joey-heavy text-fd-h1 text-center mb-10 md:mb-14 ${sectionTitleColor}`}>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDPricingCardBlock: Block = {
|
export const FDPricingCardBlock: Block = {
|
||||||
slug: 'fdPricingCard',
|
slug: 'fdPricingCard',
|
||||||
@ -131,5 +132,6 @@ export const FDPricingCardBlock: Block = {
|
|||||||
{ label: 'Gul', value: 'yellow' },
|
{ label: 'Gul', value: 'yellow' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -82,6 +82,7 @@ export const FDServiceCalculatorBlockComponent: React.FC<Props> = ({
|
|||||||
fixedFees = [],
|
fixedFees = [],
|
||||||
discountPercent,
|
discountPercent,
|
||||||
discountLabel,
|
discountLabel,
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
/* ── State: one selected index per option group ──────────────────────── */
|
/* ── State: one selected index per option group ──────────────────────── */
|
||||||
const [selectedOptions, setSelectedOptions] = useState<Record<number, number>>(() => {
|
const [selectedOptions, setSelectedOptions] = useState<Record<number, number>>(() => {
|
||||||
@ -202,7 +203,7 @@ export const FDServiceCalculatorBlockComponent: React.FC<Props> = ({
|
|||||||
: `${discountPercent}% rabatt på alla resurser`
|
: `${discountPercent}% rabatt på alla resurser`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`fd-section ${bgClass}`}>
|
<section id={anchorId || undefined} className={`fd-section ${bgClass}`}>
|
||||||
<div className="fd-container">
|
<div className="fd-container">
|
||||||
|
|
||||||
{/* ── Header ──────────────────────────────────────────────────── */}
|
{/* ── Header ──────────────────────────────────────────────────── */}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDServiceCalculatorBlock: Block = {
|
export const FDServiceCalculatorBlock: Block = {
|
||||||
slug: 'fdServiceCalculator',
|
slug: 'fdServiceCalculator',
|
||||||
@ -192,5 +193,6 @@ export const FDServiceCalculatorBlock: Block = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -10,6 +10,7 @@ export const FDServiceChooserBlockComponent: React.FC<Props> = ({
|
|||||||
description,
|
description,
|
||||||
categories = [],
|
categories = [],
|
||||||
sectionBackground = 'gray',
|
sectionBackground = 'gray',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const [activeIndex, setActiveIndex] = useState(0)
|
const [activeIndex, setActiveIndex] = useState(0)
|
||||||
const [animating, setAnimating] = useState(false)
|
const [animating, setAnimating] = useState(false)
|
||||||
@ -51,7 +52,7 @@ export const FDServiceChooserBlockComponent: React.FC<Props> = ({
|
|||||||
const activeCategory = (categories ?? [])[activeIndex]
|
const activeCategory = (categories ?? [])[activeIndex]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
|
|
||||||
<div className="text-center mb-10 md:mb-12">
|
<div className="text-center mb-10 md:mb-12">
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDServiceChooserBlock: Block = {
|
export const FDServiceChooserBlock: Block = {
|
||||||
slug: 'fdServiceChooser',
|
slug: 'fdServiceChooser',
|
||||||
@ -92,5 +93,6 @@ export const FDServiceChooserBlock: Block = {
|
|||||||
{ label: 'Navy', value: 'navy' },
|
{ label: 'Navy', value: 'navy' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -15,9 +15,10 @@ export const FDServicesGridBlockComponent: React.FC<FDServicesGridBlockProps> =
|
|||||||
heading,
|
heading,
|
||||||
services,
|
services,
|
||||||
columns = '4',
|
columns = '4',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<section className="relative w-full bg-white dark:bg-fd-navy py-16 md:py-20 lg:py-[99px]">
|
<section id={anchorId || undefined} className="relative w-full bg-white dark:bg-fd-navy py-16 md:py-20 lg:py-[99px]">
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
<h2 className="font-joey-heavy text-fd-h1 text-fd-navy dark:text-fd-yellow mb-8 lg:mb-12">
|
<h2 className="font-joey-heavy text-fd-h1 text-fd-navy dark:text-fd-yellow mb-8 lg:mb-12">
|
||||||
{heading}
|
{heading}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDServicesGridBlock: Block = {
|
export const FDServicesGridBlock: Block = {
|
||||||
slug: 'fdServicesGrid',
|
slug: 'fdServicesGrid',
|
||||||
@ -63,5 +64,6 @@ export const FDServicesGridBlock: Block = {
|
|||||||
{ label: '4 kolumner', value: '4' },
|
{ label: '4 kolumner', value: '4' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@ export const FDStatisticsBlockComponent: React.FC<Props> = ({
|
|||||||
stats = [],
|
stats = [],
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
numberColor = 'gradient',
|
numberColor = 'gradient',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const [visible, setVisible] = useState(false)
|
const [visible, setVisible] = useState(false)
|
||||||
/* Priority #8: Detect prefers-reduced-motion */
|
/* Priority #8: Detect prefers-reduced-motion */
|
||||||
@ -61,7 +62,7 @@ export const FDStatisticsBlockComponent: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`} ref={ref}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`} ref={ref}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 text-center">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 text-center">
|
||||||
{heading && (
|
{heading && (
|
||||||
<h2 className={`font-joey-heavy text-fd-h1 mb-12 md:mb-16 ${titleClass}`}>
|
<h2 className={`font-joey-heavy text-fd-h1 mb-12 md:mb-16 ${titleClass}`}>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDStatisticsBlock: Block = {
|
export const FDStatisticsBlock: Block = {
|
||||||
slug: 'fdStatistics',
|
slug: 'fdStatistics',
|
||||||
@ -69,5 +70,6 @@ export const FDStatisticsBlock: Block = {
|
|||||||
{ label: 'Vit', value: 'white' },
|
{ label: 'Vit', value: 'white' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -48,6 +48,7 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
|
|||||||
columns = '3',
|
columns = '3',
|
||||||
cardStyle = 'navy',
|
cardStyle = 'navy',
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const sectionBg = sectionBgMap[sectionBackground ?? 'white'] || sectionBgMap.white
|
const sectionBg = sectionBgMap[sectionBackground ?? 'white'] || sectionBgMap.white
|
||||||
const card = cardMap[cardStyle ?? 'navy'] || cardMap.navy
|
const card = cardMap[cardStyle ?? 'navy'] || cardMap.navy
|
||||||
@ -55,7 +56,7 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
|
|||||||
const isNavySection = sectionBackground === 'navy'
|
const isNavySection = sectionBackground === 'navy'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
|
|
||||||
{(heading || subheading) && (
|
{(heading || subheading) && (
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDTeamBlock: Block = {
|
export const FDTeamBlock: Block = {
|
||||||
slug: 'fdTeam',
|
slug: 'fdTeam',
|
||||||
@ -104,5 +105,6 @@ export const FDTeamBlock: Block = {
|
|||||||
{ label: 'Navy', value: 'navy' },
|
{ label: 'Navy', value: 'navy' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -26,6 +26,7 @@ export const FDTechPropertiesBlockComponent: React.FC<FDTechPropertiesBlockProps
|
|||||||
sectionBackground = 'navy',
|
sectionBackground = 'navy',
|
||||||
categoryColor = 'white',
|
categoryColor = 'white',
|
||||||
valueColor = 'yellow',
|
valueColor = 'yellow',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const bg = bgMap[sectionBackground || 'navy']
|
const bg = bgMap[sectionBackground || 'navy']
|
||||||
const catColor = catColorMap[categoryColor || 'white']
|
const catColor = catColorMap[categoryColor || 'white']
|
||||||
@ -35,7 +36,7 @@ export const FDTechPropertiesBlockComponent: React.FC<FDTechPropertiesBlockProps
|
|||||||
count <= 2 ? 'grid-cols-2' : count === 3 ? 'grid-cols-3' : 'grid-cols-2 md:grid-cols-4'
|
count <= 2 ? 'grid-cols-2' : count === 3 ? 'grid-cols-3' : 'grid-cols-2 md:grid-cols-4'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
<div className={`grid ${gridCols} gap-8 md:gap-12`}>
|
<div className={`grid ${gridCols} gap-8 md:gap-12`}>
|
||||||
{properties?.map((prop, index) => (
|
{properties?.map((prop, index) => (
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDTechPropertiesBlock: Block = {
|
export const FDTechPropertiesBlock: Block = {
|
||||||
slug: 'fdTechProperties',
|
slug: 'fdTechProperties',
|
||||||
@ -68,5 +69,6 @@ export const FDTechPropertiesBlock: Block = {
|
|||||||
{ label: 'Navy', value: 'navy' },
|
{ label: 'Navy', value: 'navy' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -57,12 +57,13 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
|
|||||||
testimonials,
|
testimonials,
|
||||||
layout = 'grid',
|
layout = 'grid',
|
||||||
sectionBackground = 'gray',
|
sectionBackground = 'gray',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = bgMap[sectionBackground ?? 'gray'] || bgMap.gray
|
const theme = bgMap[sectionBackground ?? 'gray'] || bgMap.gray
|
||||||
const isFeatured = layout === 'featured'
|
const isFeatured = layout === 'featured'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${theme.section}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${theme.section}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
|
|
||||||
{heading && (
|
{heading && (
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDTestimonialBlock: Block = {
|
export const FDTestimonialBlock: Block = {
|
||||||
slug: 'fdTestimonial',
|
slug: 'fdTestimonial',
|
||||||
@ -85,5 +86,6 @@ export const FDTestimonialBlock: Block = {
|
|||||||
{ label: 'Navy', value: 'navy' },
|
{ label: 'Navy', value: 'navy' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -41,6 +41,7 @@ export const FDTextBlockComponent: React.FC<FDTextBlockProps> = ({
|
|||||||
textColor = 'navy',
|
textColor = 'navy',
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
maxWidth = 'wide',
|
maxWidth = 'wide',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const bg = bgMap[sectionBackground || 'white']
|
const bg = bgMap[sectionBackground || 'white']
|
||||||
const align = alignMap[alignment || 'left']
|
const align = alignMap[alignment || 'left']
|
||||||
@ -52,7 +53,7 @@ export const FDTextBlockComponent: React.FC<FDTextBlockProps> = ({
|
|||||||
if (!heading && !subheading && !body) return null
|
if (!heading && !subheading && !body) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
<div className={`${width} ${containerAlign} ${align} flex flex-col gap-4 md:gap-6`}>
|
<div className={`${width} ${containerAlign} ${align} flex flex-col gap-4 md:gap-6`}>
|
||||||
{heading && (
|
{heading && (
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDTextBlock: Block = {
|
export const FDTextBlock: Block = {
|
||||||
slug: 'fdText',
|
slug: 'fdText',
|
||||||
@ -74,5 +75,6 @@ export const FDTextBlock: Block = {
|
|||||||
{ label: 'Full', value: 'full' },
|
{ label: 'Full', value: 'full' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -42,6 +42,7 @@ export const FDUspChecklistBlockComponent: React.FC<FDUspChecklistBlockProps> =
|
|||||||
checkColor = 'navy',
|
checkColor = 'navy',
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
textColor = 'navy',
|
textColor = 'navy',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const bg = bgMap[sectionBackground || 'white']
|
const bg = bgMap[sectionBackground || 'white']
|
||||||
const headClr = headingMap[textColor || 'navy']
|
const headClr = headingMap[textColor || 'navy']
|
||||||
@ -76,7 +77,7 @@ export const FDUspChecklistBlockComponent: React.FC<FDUspChecklistBlockProps> =
|
|||||||
) : null
|
) : null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col lg:flex-row items-center gap-10 lg:gap-16">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col lg:flex-row items-center gap-10 lg:gap-16">
|
||||||
{imagePosition === 'left' ? <>{imageContent}{textContent}</> : <>{textContent}{imageContent}</>}
|
{imagePosition === 'left' ? <>{imageContent}{textContent}</> : <>{textContent}{imageContent}</>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDUspChecklistBlock: Block = {
|
export const FDUspChecklistBlock: Block = {
|
||||||
slug: 'fdUspChecklist',
|
slug: 'fdUspChecklist',
|
||||||
@ -80,5 +81,6 @@ export const FDUspChecklistBlock: Block = {
|
|||||||
{ label: 'Vit', value: 'white' },
|
{ label: 'Vit', value: 'white' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -52,6 +52,7 @@ export const FDUspTableBlockComponent: React.FC<FDUspTableBlockProps> = ({
|
|||||||
checkColor = 'navy',
|
checkColor = 'navy',
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
textColor = 'navy',
|
textColor = 'navy',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const bg = bgMap[sectionBackground || 'white']
|
const bg = bgMap[sectionBackground || 'white']
|
||||||
const headClr = headingMap[textColor || 'navy']
|
const headClr = headingMap[textColor || 'navy']
|
||||||
@ -60,7 +61,7 @@ export const FDUspTableBlockComponent: React.FC<FDUspTableBlockProps> = ({
|
|||||||
const border = borderMap[textColor || 'navy']
|
const border = borderMap[textColor || 'navy']
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
{heading && (
|
{heading && (
|
||||||
<h2 className={`font-joey-heavy text-fd-h1 mb-10 md:mb-14 ${headClr}`}>{heading}</h2>
|
<h2 className={`font-joey-heavy text-fd-h1 mb-10 md:mb-14 ${headClr}`}>{heading}</h2>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDUspTableBlock: Block = {
|
export const FDUspTableBlock: Block = {
|
||||||
slug: 'fdUspTable',
|
slug: 'fdUspTable',
|
||||||
@ -70,5 +71,6 @@ export const FDUspTableBlock: Block = {
|
|||||||
{ label: 'Vit', value: 'white' },
|
{ label: 'Vit', value: 'white' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -58,6 +58,7 @@ export const FDVideoBlockComponent: React.FC<FDVideoBlockProps> = ({
|
|||||||
maxWidth = 'default',
|
maxWidth = 'default',
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
textColor = 'auto',
|
textColor = 'auto',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const [isPlaying, setIsPlaying] = useState(false)
|
const [isPlaying, setIsPlaying] = useState(false)
|
||||||
|
|
||||||
@ -113,7 +114,7 @@ export const FDVideoBlockComponent: React.FC<FDVideoBlockProps> = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`relative w-full py-16 md:py-20 lg:py-[99px] ${bgClass} overflow-hidden`}>
|
<section id={anchorId || undefined} className={`relative w-full py-16 md:py-20 lg:py-[99px] ${bgClass} overflow-hidden`}>
|
||||||
<div className={`relative ${containerClass} mx-auto px-6 md:px-8`}>
|
<div className={`relative ${containerClass} mx-auto px-6 md:px-8`}>
|
||||||
|
|
||||||
{(heading || description) && (
|
{(heading || description) && (
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDVideoBlock: Block = {
|
export const FDVideoBlock: Block = {
|
||||||
slug: 'fdVideo',
|
slug: 'fdVideo',
|
||||||
@ -145,5 +146,6 @@ export const FDVideoBlock: Block = {
|
|||||||
{ label: 'Vit', value: 'white' },
|
{ label: 'Vit', value: 'white' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -82,6 +82,7 @@ export const FDVpsCalculatorBlockComponent: React.FC<FDVpsCalculatorBlockProps>
|
|||||||
showAdminFee,
|
showAdminFee,
|
||||||
adminFeeAmount,
|
adminFeeAmount,
|
||||||
additionalServices = [],
|
additionalServices = [],
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const pricing = {
|
const pricing = {
|
||||||
windows: pricingWindowsLicense ?? DEFAULT_PRICING.windows,
|
windows: pricingWindowsLicense ?? DEFAULT_PRICING.windows,
|
||||||
@ -153,7 +154,7 @@ export const FDVpsCalculatorBlockComponent: React.FC<FDVpsCalculatorBlockProps>
|
|||||||
const hasTillval = (additionalServices ?? []).length > 0
|
const hasTillval = (additionalServices ?? []).length > 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`fd-section ${bgClass}`}>
|
<section id={anchorId || undefined} className={`fd-section ${bgClass}`}>
|
||||||
<div className="fd-container">
|
<div className="fd-container">
|
||||||
|
|
||||||
{(heading || description) && (
|
{(heading || description) && (
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDVpsCalculatorBlock: Block = {
|
export const FDVpsCalculatorBlock: Block = {
|
||||||
slug: 'fdVpsCalculator',
|
slug: 'fdVpsCalculator',
|
||||||
@ -125,5 +126,6 @@ export const FDVpsCalculatorBlock: Block = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -33,6 +33,7 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
|||||||
cardBackground = 'navy',
|
cardBackground = 'navy',
|
||||||
buttonColor = 'yellow',
|
buttonColor = 'yellow',
|
||||||
sectionBackground = 'white',
|
sectionBackground = 'white',
|
||||||
|
anchorId,
|
||||||
}) => {
|
}) => {
|
||||||
const card = cardBgMap[cardBackground || 'navy']
|
const card = cardBgMap[cardBackground || 'navy']
|
||||||
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
||||||
@ -41,7 +42,7 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
|||||||
const hasImage = media && typeof media === 'object' && media.url
|
const hasImage = media && typeof media === 'object' && media.url
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
<div className={`${card.bg} ${cardRadius} overflow-hidden flex flex-col lg:flex-row`}>
|
<div className={`${card.bg} ${cardRadius} overflow-hidden flex flex-col lg:flex-row`}>
|
||||||
<div className="flex-1 flex flex-col justify-center gap-5 md:gap-6 px-8 md:px-14 lg:px-16 py-12 md:py-16">
|
<div className="flex-1 flex flex-col justify-center gap-5 md:gap-6 px-8 md:px-14 lg:px-16 py-12 md:py-16">
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
export const FDWideCardBlock: Block = {
|
export const FDWideCardBlock: Block = {
|
||||||
slug: 'fdWideCard',
|
slug: 'fdWideCard',
|
||||||
@ -75,5 +76,6 @@ export const FDWideCardBlock: Block = {
|
|||||||
{ label: 'Navy', value: 'navy' },
|
{ label: 'Navy', value: 'navy' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
anchorField,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
47
src/components/BackToTop/index.tsx
Normal file
47
src/components/BackToTop/index.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useState, useEffect, useCallback } from 'react'
|
||||||
|
import { ChevronUpIcon } from 'lucide-react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Floating "back to top" button.
|
||||||
|
* Appears after scrolling down 400px, smooth-scrolls to top on click.
|
||||||
|
* Styled to match FD design system.
|
||||||
|
*/
|
||||||
|
export const BackToTop: React.FC = () => {
|
||||||
|
const [visible, setVisible] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onScroll = () => {
|
||||||
|
setVisible(window.scrollY > 400)
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', onScroll, { passive: true })
|
||||||
|
return () => window.removeEventListener('scroll', onScroll)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const scrollToTop = useCallback(() => {
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={scrollToTop}
|
||||||
|
aria-label="Tillbaka till toppen"
|
||||||
|
className={`
|
||||||
|
fixed bottom-6 right-6 z-40
|
||||||
|
w-11 h-11 rounded-full
|
||||||
|
bg-fd-navy/80 backdrop-blur-sm
|
||||||
|
text-white hover:text-fd-yellow
|
||||||
|
border border-white/10 hover:border-fd-yellow/30
|
||||||
|
shadow-lg hover:shadow-xl
|
||||||
|
flex items-center justify-center
|
||||||
|
transition-all duration-300 ease-in-out cursor-pointer
|
||||||
|
${visible
|
||||||
|
? 'opacity-100 translate-y-0 pointer-events-auto'
|
||||||
|
: 'opacity-0 translate-y-4 pointer-events-none'
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<ChevronUpIcon className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
29
src/fields/anchorField.ts
Normal file
29
src/fields/anchorField.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import type { Field } from 'payload'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared anchorId field for all FD blocks.
|
||||||
|
* Allows editors to set an anchor link ID on any section,
|
||||||
|
* enabling direct linking like /page#section-name
|
||||||
|
*
|
||||||
|
* Usage in block config:
|
||||||
|
* import { anchorField } from '@/fields/anchorField'
|
||||||
|
* fields: [ ...contentFields, anchorField ]
|
||||||
|
*
|
||||||
|
* Usage in block component:
|
||||||
|
* <section id={anchorId || undefined} ...>
|
||||||
|
*/
|
||||||
|
export const anchorField: Field = {
|
||||||
|
name: 'anchorId',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Ankarlänk-ID',
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
'Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.',
|
||||||
|
position: 'sidebar',
|
||||||
|
},
|
||||||
|
validate: (value: unknown) => {
|
||||||
|
if (!value) return true
|
||||||
|
if (typeof value === 'string' && /^[a-z0-9][a-z0-9-]*$/.test(value)) return true
|
||||||
|
return 'Använd bara små bokstäver (a-z), siffror (0-9) och bindestreck (-). Inga mellanslag.'
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -233,6 +233,10 @@ export interface FDHeroBlock {
|
|||||||
* Ignoreras om bakgrundsbild är vald
|
* Ignoreras om bakgrundsbild är vald
|
||||||
*/
|
*/
|
||||||
theme?: ('light' | 'dark') | null;
|
theme?: ('light' | 'dark') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdHero';
|
blockType: 'fdHero';
|
||||||
@ -305,6 +309,10 @@ export interface FDCtaSideImageBlock {
|
|||||||
| null;
|
| null;
|
||||||
imagePosition?: ('right' | 'left') | null;
|
imagePosition?: ('right' | 'left') | null;
|
||||||
theme?: ('light' | 'dark') | null;
|
theme?: ('light' | 'dark') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdCtaSideImage';
|
blockType: 'fdCtaSideImage';
|
||||||
@ -319,6 +327,10 @@ export interface FDFeatureAnnouncementBlock {
|
|||||||
ctaText?: string | null;
|
ctaText?: string | null;
|
||||||
ctaLink?: string | null;
|
ctaLink?: string | null;
|
||||||
theme?: ('gray' | 'light' | 'dark') | null;
|
theme?: ('gray' | 'light' | 'dark') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdFeatureAnnouncement';
|
blockType: 'fdFeatureAnnouncement';
|
||||||
@ -339,6 +351,10 @@ export interface FDServicesGridBlock {
|
|||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
columns?: ('2' | '3' | '4') | null;
|
columns?: ('2' | '3' | '4') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdServicesGrid';
|
blockType: 'fdServicesGrid';
|
||||||
@ -360,6 +376,10 @@ export interface FDContactBlock {
|
|||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdContact';
|
blockType: 'fdContact';
|
||||||
@ -392,6 +412,10 @@ export interface FDFaqBlock {
|
|||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
theme?: ('gray' | 'light' | 'dark') | null;
|
theme?: ('gray' | 'light' | 'dark') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdFaq';
|
blockType: 'fdFaq';
|
||||||
@ -430,6 +454,10 @@ export interface FDCardGridBlock {
|
|||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
sectionBackground?: ('white' | 'navy' | 'gray') | null;
|
sectionBackground?: ('white' | 'navy' | 'gray') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdCardGrid';
|
blockType: 'fdCardGrid';
|
||||||
@ -472,6 +500,10 @@ export interface FDPricingCardBlock {
|
|||||||
* Färg på blockrubriken
|
* Färg på blockrubriken
|
||||||
*/
|
*/
|
||||||
titleColor?: ('navy' | 'white' | 'yellow') | null;
|
titleColor?: ('navy' | 'white' | 'yellow') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdPricingCard';
|
blockType: 'fdPricingCard';
|
||||||
@ -525,6 +557,10 @@ export interface FDUspChecklistBlock {
|
|||||||
checkColor?: ('navy' | 'yellow' | 'gray') | null;
|
checkColor?: ('navy' | 'yellow' | 'gray') | null;
|
||||||
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
||||||
textColor?: ('navy' | 'white') | null;
|
textColor?: ('navy' | 'white') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdUspChecklist';
|
blockType: 'fdUspChecklist';
|
||||||
@ -542,6 +578,10 @@ export interface FDWideCardBlock {
|
|||||||
cardBackground?: ('navy' | 'yellow' | 'gray' | 'white') | null;
|
cardBackground?: ('navy' | 'yellow' | 'gray' | 'white') | null;
|
||||||
buttonColor?: ('yellow' | 'navy' | 'white') | null;
|
buttonColor?: ('yellow' | 'navy' | 'white') | null;
|
||||||
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdWideCard';
|
blockType: 'fdWideCard';
|
||||||
@ -567,6 +607,10 @@ export interface FDTechPropertiesBlock {
|
|||||||
sectionBackground?: ('navy' | 'white' | 'gray' | 'yellow') | null;
|
sectionBackground?: ('navy' | 'white' | 'gray' | 'yellow') | null;
|
||||||
categoryColor?: ('white' | 'navy') | null;
|
categoryColor?: ('white' | 'navy') | null;
|
||||||
valueColor?: ('yellow' | 'white' | 'navy') | null;
|
valueColor?: ('yellow' | 'white' | 'navy') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdTechProperties';
|
blockType: 'fdTechProperties';
|
||||||
@ -604,6 +648,10 @@ export interface FDUspTableBlock {
|
|||||||
checkColor?: ('navy' | 'yellow' | 'gray') | null;
|
checkColor?: ('navy' | 'yellow' | 'gray') | null;
|
||||||
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
||||||
textColor?: ('navy' | 'white') | null;
|
textColor?: ('navy' | 'white') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdUspTable';
|
blockType: 'fdUspTable';
|
||||||
@ -623,6 +671,10 @@ export interface FDHeaderTextImageBlock {
|
|||||||
textAlign?: ('left' | 'center') | null;
|
textAlign?: ('left' | 'center') | null;
|
||||||
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
||||||
textColor?: ('navy' | 'white') | null;
|
textColor?: ('navy' | 'white') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdHeaderTextImage';
|
blockType: 'fdHeaderTextImage';
|
||||||
@ -662,6 +714,10 @@ export interface FDContactFormBlock {
|
|||||||
*/
|
*/
|
||||||
authToken?: string | null;
|
authToken?: string | null;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdContactForm';
|
blockType: 'fdContactForm';
|
||||||
@ -881,6 +937,10 @@ export interface FDAlternateHeroBlock {
|
|||||||
image?: (number | null) | Media;
|
image?: (number | null) | Media;
|
||||||
imageCaption?: string | null;
|
imageCaption?: string | null;
|
||||||
sectionBackground?: ('white' | 'navy' | 'gray') | null;
|
sectionBackground?: ('white' | 'navy' | 'gray') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdAlternateHero';
|
blockType: 'fdAlternateHero';
|
||||||
@ -906,6 +966,10 @@ export interface FDStatisticsBlock {
|
|||||||
| null;
|
| null;
|
||||||
sectionBackground?: ('white' | 'navy' | 'gray') | null;
|
sectionBackground?: ('white' | 'navy' | 'gray') | null;
|
||||||
numberColor?: ('gradient' | 'yellow' | 'mint' | 'navy' | 'white') | null;
|
numberColor?: ('gradient' | 'yellow' | 'mint' | 'navy' | 'white') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdStatistics';
|
blockType: 'fdStatistics';
|
||||||
@ -996,6 +1060,10 @@ export interface FDServiceChooserBlock {
|
|||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
sectionBackground?: ('gray' | 'white' | 'navy') | null;
|
sectionBackground?: ('gray' | 'white' | 'navy') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdServiceChooser';
|
blockType: 'fdServiceChooser';
|
||||||
@ -1050,6 +1118,10 @@ export interface FDDataTableBlock {
|
|||||||
* Gör den första kolumnen fet — användbart för namnkolumner.
|
* Gör den första kolumnen fet — användbart för namnkolumner.
|
||||||
*/
|
*/
|
||||||
firstColumnBold?: boolean | null;
|
firstColumnBold?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdDataTable';
|
blockType: 'fdDataTable';
|
||||||
@ -1093,6 +1165,10 @@ export interface FDVpsCalculatorBlock {
|
|||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdVpsCalculator';
|
blockType: 'fdVpsCalculator';
|
||||||
@ -1179,6 +1255,10 @@ export interface FDServiceCalculatorBlock {
|
|||||||
* T.ex. "{percent}% rabatt på alla resurser". Använd {percent} som variabel.
|
* T.ex. "{percent}% rabatt på alla resurser". Använd {percent} som variabel.
|
||||||
*/
|
*/
|
||||||
discountLabel?: string | null;
|
discountLabel?: string | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdServiceCalculator';
|
blockType: 'fdServiceCalculator';
|
||||||
@ -1230,6 +1310,10 @@ export interface FDTextBlock {
|
|||||||
textColor?: ('navy' | 'white' | 'yellow') | null;
|
textColor?: ('navy' | 'white' | 'yellow') | null;
|
||||||
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow') | null;
|
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow') | null;
|
||||||
maxWidth?: ('narrow' | 'medium' | 'wide' | 'full') | null;
|
maxWidth?: ('narrow' | 'medium' | 'wide' | 'full') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdText';
|
blockType: 'fdText';
|
||||||
@ -1276,6 +1360,10 @@ export interface FDCodeEmbedBlock {
|
|||||||
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow' | 'transparent') | null;
|
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow' | 'transparent') | null;
|
||||||
textColor?: ('auto' | 'navy' | 'white') | null;
|
textColor?: ('auto' | 'navy' | 'white') | null;
|
||||||
embedBackground?: ('none' | 'card' | 'navy-card') | null;
|
embedBackground?: ('none' | 'card' | 'navy-card') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdCodeEmbed';
|
blockType: 'fdCodeEmbed';
|
||||||
@ -1313,6 +1401,10 @@ export interface FDVideoBlock {
|
|||||||
maxWidth?: ('default' | 'narrow' | 'wide') | null;
|
maxWidth?: ('default' | 'narrow' | 'wide') | null;
|
||||||
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow' | 'transparent') | null;
|
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow' | 'transparent') | null;
|
||||||
textColor?: ('auto' | 'navy' | 'white') | null;
|
textColor?: ('auto' | 'navy' | 'white') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdVideo';
|
blockType: 'fdVideo';
|
||||||
@ -1334,6 +1426,10 @@ export interface FDCtaBannerBlock {
|
|||||||
sectionBackground?: ('yellow' | 'navy' | 'gray' | 'white') | null;
|
sectionBackground?: ('yellow' | 'navy' | 'gray' | 'white') | null;
|
||||||
alignment?: ('center' | 'left') | null;
|
alignment?: ('center' | 'left') | null;
|
||||||
size?: ('small' | 'medium' | 'large') | null;
|
size?: ('small' | 'medium' | 'large') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdCtaBanner';
|
blockType: 'fdCtaBanner';
|
||||||
@ -1365,6 +1461,10 @@ export interface FDTestimonialBlock {
|
|||||||
| null;
|
| null;
|
||||||
layout?: ('grid' | 'featured') | null;
|
layout?: ('grid' | 'featured') | null;
|
||||||
sectionBackground?: ('gray' | 'white' | 'navy') | null;
|
sectionBackground?: ('gray' | 'white' | 'navy') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdTestimonial';
|
blockType: 'fdTestimonial';
|
||||||
@ -1396,6 +1496,10 @@ export interface FDTeamBlock {
|
|||||||
columns?: ('2' | '3' | '4') | null;
|
columns?: ('2' | '3' | '4') | null;
|
||||||
cardStyle?: ('navy' | 'white' | 'gray') | null;
|
cardStyle?: ('navy' | 'white' | 'gray') | null;
|
||||||
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
||||||
|
/**
|
||||||
|
* Valfritt. Används för att länka direkt till denna sektion, t.ex. "priser" ger /sida#priser. Använd bara små bokstäver, siffror och bindestreck.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdTeam';
|
blockType: 'fdTeam';
|
||||||
@ -1808,6 +1912,7 @@ export interface FDHeroBlockSelect<T extends boolean = true> {
|
|||||||
overlayOpacity?: T;
|
overlayOpacity?: T;
|
||||||
textColor?: T;
|
textColor?: T;
|
||||||
theme?: T;
|
theme?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -1824,6 +1929,7 @@ export interface FDCtaSideImageBlockSelect<T extends boolean = true> {
|
|||||||
imageOverlay?: T;
|
imageOverlay?: T;
|
||||||
imagePosition?: T;
|
imagePosition?: T;
|
||||||
theme?: T;
|
theme?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -1837,6 +1943,7 @@ export interface FDFeatureAnnouncementBlockSelect<T extends boolean = true> {
|
|||||||
ctaText?: T;
|
ctaText?: T;
|
||||||
ctaLink?: T;
|
ctaLink?: T;
|
||||||
theme?: T;
|
theme?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -1856,6 +1963,7 @@ export interface FDServicesGridBlockSelect<T extends boolean = true> {
|
|||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
columns?: T;
|
columns?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -1873,6 +1981,7 @@ export interface FDContactBlockSelect<T extends boolean = true> {
|
|||||||
link?: T;
|
link?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -1890,6 +1999,7 @@ export interface FDFaqBlockSelect<T extends boolean = true> {
|
|||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
theme?: T;
|
theme?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -1918,6 +2028,7 @@ export interface FDCardGridBlockSelect<T extends boolean = true> {
|
|||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -1947,6 +2058,7 @@ export interface FDPricingCardBlockSelect<T extends boolean = true> {
|
|||||||
buttonColor?: T;
|
buttonColor?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
titleColor?: T;
|
titleColor?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -1997,6 +2109,7 @@ export interface FDUspChecklistBlockSelect<T extends boolean = true> {
|
|||||||
checkColor?: T;
|
checkColor?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
textColor?: T;
|
textColor?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2013,6 +2126,7 @@ export interface FDWideCardBlockSelect<T extends boolean = true> {
|
|||||||
cardBackground?: T;
|
cardBackground?: T;
|
||||||
buttonColor?: T;
|
buttonColor?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2031,6 +2145,7 @@ export interface FDTechPropertiesBlockSelect<T extends boolean = true> {
|
|||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
categoryColor?: T;
|
categoryColor?: T;
|
||||||
valueColor?: T;
|
valueColor?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2050,6 +2165,7 @@ export interface FDUspTableBlockSelect<T extends boolean = true> {
|
|||||||
checkColor?: T;
|
checkColor?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
textColor?: T;
|
textColor?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2066,6 +2182,7 @@ export interface FDHeaderTextImageBlockSelect<T extends boolean = true> {
|
|||||||
textAlign?: T;
|
textAlign?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
textColor?: T;
|
textColor?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2091,6 +2208,7 @@ export interface FDContactFormBlockSelect<T extends boolean = true> {
|
|||||||
endpoint?: T;
|
endpoint?: T;
|
||||||
authToken?: T;
|
authToken?: T;
|
||||||
};
|
};
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2131,6 +2249,7 @@ export interface FDAlternateHeroBlockSelect<T extends boolean = true> {
|
|||||||
image?: T;
|
image?: T;
|
||||||
imageCaption?: T;
|
imageCaption?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2149,6 +2268,7 @@ export interface FDStatisticsBlockSelect<T extends boolean = true> {
|
|||||||
};
|
};
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
numberColor?: T;
|
numberColor?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2215,6 +2335,7 @@ export interface FDServiceChooserBlockSelect<T extends boolean = true> {
|
|||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2244,6 +2365,7 @@ export interface FDDataTableBlockSelect<T extends boolean = true> {
|
|||||||
stripeRows?: T;
|
stripeRows?: T;
|
||||||
bordered?: T;
|
bordered?: T;
|
||||||
firstColumnBold?: T;
|
firstColumnBold?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2274,6 +2396,7 @@ export interface FDVpsCalculatorBlockSelect<T extends boolean = true> {
|
|||||||
price?: T;
|
price?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2335,6 +2458,7 @@ export interface FDServiceCalculatorBlockSelect<T extends boolean = true> {
|
|||||||
};
|
};
|
||||||
discountPercent?: T;
|
discountPercent?: T;
|
||||||
discountLabel?: T;
|
discountLabel?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2370,6 +2494,7 @@ export interface FDTextBlockSelect<T extends boolean = true> {
|
|||||||
textColor?: T;
|
textColor?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
maxWidth?: T;
|
maxWidth?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2391,6 +2516,7 @@ export interface FDCodeEmbedBlockSelect<T extends boolean = true> {
|
|||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
textColor?: T;
|
textColor?: T;
|
||||||
embedBackground?: T;
|
embedBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2412,6 +2538,7 @@ export interface FDVideoBlockSelect<T extends boolean = true> {
|
|||||||
maxWidth?: T;
|
maxWidth?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
textColor?: T;
|
textColor?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2429,6 +2556,7 @@ export interface FDCtaBannerBlockSelect<T extends boolean = true> {
|
|||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
alignment?: T;
|
alignment?: T;
|
||||||
size?: T;
|
size?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2450,6 +2578,7 @@ export interface FDTestimonialBlockSelect<T extends boolean = true> {
|
|||||||
};
|
};
|
||||||
layout?: T;
|
layout?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
@ -2474,6 +2603,7 @@ export interface FDTeamBlockSelect<T extends boolean = true> {
|
|||||||
columns?: T;
|
columns?: T;
|
||||||
cardStyle?: T;
|
cardStyle?: T;
|
||||||
sectionBackground?: T;
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user