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 'tw-animate-css';
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { GeistSans } from 'geist/font/sans'
|
||||
import React from 'react'
|
||||
import { AdminBar } from '@/components/AdminBar'
|
||||
import { Footer } from '@/Footer/Component'
|
||||
import { BackToTop } from '@/components/BackToTop'
|
||||
import { Header } from '@/Header/Component'
|
||||
import { Providers } from '@/providers'
|
||||
import { InitTheme } from '@/providers/Theme/InitTheme'
|
||||
@ -67,6 +68,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
<Header />
|
||||
{children}
|
||||
<Footer />
|
||||
<BackToTop />
|
||||
<PopupAnnouncementComponent {...popupData} />
|
||||
{cookieEnabled && (
|
||||
<CookieConsent
|
||||
|
||||
@ -13,6 +13,7 @@ export const FDAlternateHeroBlockComponent: React.FC<Props> = ({
|
||||
image,
|
||||
imageCaption,
|
||||
sectionBackground = 'white',
|
||||
anchorId,
|
||||
}) => {
|
||||
const media = image as Media | undefined
|
||||
const hasImage = media && typeof media === 'object' && media.url
|
||||
@ -34,7 +35,7 @@ export const FDAlternateHeroBlockComponent: React.FC<Props> = ({
|
||||
: 'text-fd-navy dark:text-white'
|
||||
|
||||
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 */}
|
||||
<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}`}>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDAlternateHeroBlock: Block = {
|
||||
slug: 'fdAlternateHero',
|
||||
@ -78,5 +79,6 @@ export const FDAlternateHeroBlock: Block = {
|
||||
{ label: 'Grå', value: 'gray' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
@ -71,13 +71,14 @@ export const FDCardGridBlockComponent: React.FC<FDCardGridBlockProps> = ({
|
||||
cardStyle = 'outlined',
|
||||
cards,
|
||||
sectionBackground = 'white',
|
||||
anchorId,
|
||||
}) => {
|
||||
const style = cardStyleMap[cardStyle] || cardStyleMap.outlined
|
||||
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
||||
const gridCols = layoutGridMap[layout] || layoutGridMap['1-1-1']
|
||||
|
||||
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={`grid grid-cols-1 md:grid-cols-2 ${gridCols} gap-4 md:gap-6`}>
|
||||
{cards?.map((card, index) => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDCardGridBlock: Block = {
|
||||
slug: 'fdCardGrid',
|
||||
@ -134,5 +135,6 @@ export const FDCardGridBlock: Block = {
|
||||
{ label: 'Grå', value: 'gray' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -36,6 +36,7 @@ export const FDCodeEmbedBlockComponent: React.FC<FDCodeEmbedBlockProps> = ({
|
||||
sectionBackground = 'white',
|
||||
textColor = 'auto',
|
||||
embedBackground = 'none',
|
||||
anchorId,
|
||||
}) => {
|
||||
const customCodeRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@ -96,7 +97,7 @@ export const FDCodeEmbedBlockComponent: React.FC<FDCodeEmbedBlockProps> = ({
|
||||
: ''
|
||||
|
||||
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`}>
|
||||
{(heading || description) && (
|
||||
<div className="mb-8 md:mb-12 text-center">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDCodeEmbedBlock: Block = {
|
||||
slug: 'fdCodeEmbed',
|
||||
@ -151,5 +152,6 @@ export const FDCodeEmbedBlock: Block = {
|
||||
{ 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> = ({
|
||||
heading,
|
||||
contactMethods,
|
||||
anchorId,
|
||||
}) => {
|
||||
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">
|
||||
<h2 className="w-full font-joey-heavy text-fd-h1 text-fd-yellow text-center">
|
||||
{heading}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDContactBlock: Block = {
|
||||
slug: 'fdContact',
|
||||
@ -48,5 +49,6 @@ export const FDContactBlock: Block = {
|
||||
},
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,7 @@ export const FDContactFormBlockComponent: React.FC<FDContactFormBlockProps> = ({
|
||||
sectionBackground = 'white',
|
||||
layout = 'standard',
|
||||
externalApi,
|
||||
anchorId,
|
||||
}) => {
|
||||
const [formData, setFormData] = useState<Record<string, string>>({})
|
||||
const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle')
|
||||
@ -278,7 +279,7 @@ export const FDContactFormBlockComponent: React.FC<FDContactFormBlockProps> = ({
|
||||
|
||||
if (status === 'sent') {
|
||||
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={`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}>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDContactFormBlock: Block = {
|
||||
slug: 'fdContactForm',
|
||||
@ -153,5 +154,6 @@ export const FDContactFormBlock: Block = {
|
||||
},
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -51,13 +51,14 @@ export const FDCtaBannerBlockComponent: React.FC<FDCtaBannerBlockProps> = ({
|
||||
sectionBackground = 'yellow',
|
||||
alignment = 'center',
|
||||
size = 'medium',
|
||||
anchorId,
|
||||
}) => {
|
||||
const theme = bgMap[sectionBackground ?? 'yellow'] || bgMap.yellow
|
||||
const sizing = sizeMap[size ?? 'medium'] || sizeMap.medium
|
||||
const isCenter = alignment === 'center'
|
||||
|
||||
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={`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 { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDCtaBannerBlock: Block = {
|
||||
slug: 'fdCtaBanner',
|
||||
@ -84,5 +85,6 @@ export const FDCtaBannerBlock: Block = {
|
||||
{ label: 'Stor', value: 'large' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,7 @@ export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> =
|
||||
imageOverlay = 'none',
|
||||
imagePosition = 'right',
|
||||
theme = 'dark',
|
||||
anchorId,
|
||||
}) => {
|
||||
const isDark = theme === 'dark'
|
||||
const hasImage = !!image
|
||||
@ -79,7 +80,7 @@ export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> =
|
||||
) : null
|
||||
|
||||
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">
|
||||
{imagePosition === 'left' ? (
|
||||
<>{imageContent}{textContent}</>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDCtaSideImageBlock: Block = {
|
||||
slug: 'fdCtaSideImage',
|
||||
@ -83,5 +84,6 @@ export const FDCtaSideImageBlock: Block = {
|
||||
{ label: 'Mörkt', value: 'dark' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -83,6 +83,7 @@ export const FDDataTableBlockComponent: React.FC<Props> = ({
|
||||
stripeRows = true,
|
||||
bordered = false,
|
||||
firstColumnBold = false,
|
||||
anchorId,
|
||||
}) => {
|
||||
const [tableData, setTableData] = useState<TableData | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
@ -166,7 +167,7 @@ export const FDDataTableBlockComponent: React.FC<Props> = ({
|
||||
: ''
|
||||
|
||||
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">
|
||||
|
||||
{(heading || description) && (
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDDataTableBlock: Block = {
|
||||
slug: 'fdDataTable',
|
||||
@ -143,5 +144,6 @@ export const FDDataTableBlock: Block = {
|
||||
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,
|
||||
items,
|
||||
theme = 'gray',
|
||||
anchorId,
|
||||
}) => {
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(null)
|
||||
/* 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'
|
||||
|
||||
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">
|
||||
<h2 className={`w-full max-w-[550px] font-joey-heavy text-fd-h1 ${headingColor}`}>
|
||||
{heading}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDFaqBlock: Block = {
|
||||
slug: 'fdFaq',
|
||||
@ -50,5 +51,6 @@ export const FDFaqBlock: Block = {
|
||||
{ label: 'Mörkt', value: 'dark' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ export const FDFeatureAnnouncementBlockComponent: React.FC<FDFeatureAnnouncement
|
||||
ctaText,
|
||||
ctaLink = '#',
|
||||
theme = 'gray',
|
||||
anchorId,
|
||||
}) => {
|
||||
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
|
||||
|
||||
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">
|
||||
<h2
|
||||
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 { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDFeatureAnnouncementBlock: Block = {
|
||||
slug: 'fdFeatureAnnouncement',
|
||||
@ -47,5 +48,6 @@ export const FDFeatureAnnouncementBlock: Block = {
|
||||
{ label: 'Mörkt', value: 'dark' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -47,6 +47,7 @@ export const FDHeaderTextImageBlockComponent: React.FC<FDHeaderTextImageBlockPro
|
||||
textAlign = 'center',
|
||||
sectionBackground = 'white',
|
||||
textColor = 'navy',
|
||||
anchorId,
|
||||
}) => {
|
||||
const bg = bgMap[sectionBackground || 'white']
|
||||
const headClr = headingMap[textColor || 'navy']
|
||||
@ -57,7 +58,7 @@ export const FDHeaderTextImageBlockComponent: React.FC<FDHeaderTextImageBlockPro
|
||||
const media = image as Media
|
||||
|
||||
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">
|
||||
{(heading || body) && (
|
||||
<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 { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDHeaderTextImageBlock: Block = {
|
||||
slug: 'fdHeaderTextImage',
|
||||
@ -87,5 +88,6 @@ export const FDHeaderTextImageBlock: Block = {
|
||||
{ label: 'Vit', value: 'white' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
||||
overlayOpacity = '50',
|
||||
textColor = 'auto',
|
||||
theme = 'light',
|
||||
anchorId,
|
||||
}) => {
|
||||
const media = backgroundImage as Media | undefined
|
||||
const hasBgImage = Boolean(media?.url)
|
||||
@ -39,7 +40,7 @@ export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
||||
const secondaryOnDark = textColor === 'navy' ? false : isDark
|
||||
|
||||
return (
|
||||
<section
|
||||
<section id={anchorId || undefined}
|
||||
className={`relative w-full py-16 md:py-20 lg:py-[99px] ${
|
||||
hasBgImage ? '' : isDark ? 'bg-fd-navy' : 'bg-white dark:bg-fd-navy'
|
||||
} overflow-hidden`}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDHeroBlock: Block = {
|
||||
slug: 'fdHero',
|
||||
@ -109,5 +110,6 @@ export const FDHeroBlock: Block = {
|
||||
{ label: 'Mörkt', value: 'dark' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
@ -78,6 +78,7 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
|
||||
buttonColor = 'yellow',
|
||||
sectionBackground = 'white',
|
||||
titleColor = 'navy',
|
||||
anchorId,
|
||||
}) => {
|
||||
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
||||
const sectionTitleColor = titleColorMap[titleColor || 'navy']
|
||||
@ -88,7 +89,7 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
|
||||
const outlineOnDark = style.isDark
|
||||
|
||||
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">
|
||||
{sectionTitle && (
|
||||
<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 { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDPricingCardBlock: Block = {
|
||||
slug: 'fdPricingCard',
|
||||
@ -131,5 +132,6 @@ export const FDPricingCardBlock: Block = {
|
||||
{ label: 'Gul', value: 'yellow' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -82,6 +82,7 @@ export const FDServiceCalculatorBlockComponent: React.FC<Props> = ({
|
||||
fixedFees = [],
|
||||
discountPercent,
|
||||
discountLabel,
|
||||
anchorId,
|
||||
}) => {
|
||||
/* ── State: one selected index per option group ──────────────────────── */
|
||||
const [selectedOptions, setSelectedOptions] = useState<Record<number, number>>(() => {
|
||||
@ -202,7 +203,7 @@ export const FDServiceCalculatorBlockComponent: React.FC<Props> = ({
|
||||
: `${discountPercent}% rabatt på alla resurser`
|
||||
|
||||
return (
|
||||
<section className={`fd-section ${bgClass}`}>
|
||||
<section id={anchorId || undefined} className={`fd-section ${bgClass}`}>
|
||||
<div className="fd-container">
|
||||
|
||||
{/* ── Header ──────────────────────────────────────────────────── */}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDServiceCalculatorBlock: Block = {
|
||||
slug: 'fdServiceCalculator',
|
||||
@ -192,5 +193,6 @@ export const FDServiceCalculatorBlock: Block = {
|
||||
},
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ export const FDServiceChooserBlockComponent: React.FC<Props> = ({
|
||||
description,
|
||||
categories = [],
|
||||
sectionBackground = 'gray',
|
||||
anchorId,
|
||||
}) => {
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
const [animating, setAnimating] = useState(false)
|
||||
@ -51,7 +52,7 @@ export const FDServiceChooserBlockComponent: React.FC<Props> = ({
|
||||
const activeCategory = (categories ?? [])[activeIndex]
|
||||
|
||||
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="text-center mb-10 md:mb-12">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDServiceChooserBlock: Block = {
|
||||
slug: 'fdServiceChooser',
|
||||
@ -92,5 +93,6 @@ export const FDServiceChooserBlock: Block = {
|
||||
{ label: 'Navy', value: 'navy' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
@ -15,9 +15,10 @@ export const FDServicesGridBlockComponent: React.FC<FDServicesGridBlockProps> =
|
||||
heading,
|
||||
services,
|
||||
columns = '4',
|
||||
anchorId,
|
||||
}) => {
|
||||
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">
|
||||
<h2 className="font-joey-heavy text-fd-h1 text-fd-navy dark:text-fd-yellow mb-8 lg:mb-12">
|
||||
{heading}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDServicesGridBlock: Block = {
|
||||
slug: 'fdServicesGrid',
|
||||
@ -63,5 +64,6 @@ export const FDServicesGridBlock: Block = {
|
||||
{ label: '4 kolumner', value: '4' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ export const FDStatisticsBlockComponent: React.FC<Props> = ({
|
||||
stats = [],
|
||||
sectionBackground = 'white',
|
||||
numberColor = 'gradient',
|
||||
anchorId,
|
||||
}) => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
/* Priority #8: Detect prefers-reduced-motion */
|
||||
@ -61,7 +62,7 @@ export const FDStatisticsBlockComponent: React.FC<Props> = ({
|
||||
}
|
||||
|
||||
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">
|
||||
{heading && (
|
||||
<h2 className={`font-joey-heavy text-fd-h1 mb-12 md:mb-16 ${titleClass}`}>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDStatisticsBlock: Block = {
|
||||
slug: 'fdStatistics',
|
||||
@ -69,5 +70,6 @@ export const FDStatisticsBlock: Block = {
|
||||
{ label: 'Vit', value: 'white' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
@ -48,6 +48,7 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
|
||||
columns = '3',
|
||||
cardStyle = 'navy',
|
||||
sectionBackground = 'white',
|
||||
anchorId,
|
||||
}) => {
|
||||
const sectionBg = sectionBgMap[sectionBackground ?? 'white'] || sectionBgMap.white
|
||||
const card = cardMap[cardStyle ?? 'navy'] || cardMap.navy
|
||||
@ -55,7 +56,7 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
|
||||
const isNavySection = sectionBackground === 'navy'
|
||||
|
||||
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">
|
||||
|
||||
{(heading || subheading) && (
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDTeamBlock: Block = {
|
||||
slug: 'fdTeam',
|
||||
@ -104,5 +105,6 @@ export const FDTeamBlock: Block = {
|
||||
{ label: 'Navy', value: 'navy' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,7 @@ export const FDTechPropertiesBlockComponent: React.FC<FDTechPropertiesBlockProps
|
||||
sectionBackground = 'navy',
|
||||
categoryColor = 'white',
|
||||
valueColor = 'yellow',
|
||||
anchorId,
|
||||
}) => {
|
||||
const bg = bgMap[sectionBackground || 'navy']
|
||||
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'
|
||||
|
||||
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={`grid ${gridCols} gap-8 md:gap-12`}>
|
||||
{properties?.map((prop, index) => (
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDTechPropertiesBlock: Block = {
|
||||
slug: 'fdTechProperties',
|
||||
@ -68,5 +69,6 @@ export const FDTechPropertiesBlock: Block = {
|
||||
{ label: 'Navy', value: 'navy' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -57,12 +57,13 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
|
||||
testimonials,
|
||||
layout = 'grid',
|
||||
sectionBackground = 'gray',
|
||||
anchorId,
|
||||
}) => {
|
||||
const theme = bgMap[sectionBackground ?? 'gray'] || bgMap.gray
|
||||
const isFeatured = layout === 'featured'
|
||||
|
||||
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">
|
||||
|
||||
{heading && (
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDTestimonialBlock: Block = {
|
||||
slug: 'fdTestimonial',
|
||||
@ -85,5 +86,6 @@ export const FDTestimonialBlock: Block = {
|
||||
{ label: 'Navy', value: 'navy' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,7 @@ export const FDTextBlockComponent: React.FC<FDTextBlockProps> = ({
|
||||
textColor = 'navy',
|
||||
sectionBackground = 'white',
|
||||
maxWidth = 'wide',
|
||||
anchorId,
|
||||
}) => {
|
||||
const bg = bgMap[sectionBackground || 'white']
|
||||
const align = alignMap[alignment || 'left']
|
||||
@ -52,7 +53,7 @@ export const FDTextBlockComponent: React.FC<FDTextBlockProps> = ({
|
||||
if (!heading && !subheading && !body) return null
|
||||
|
||||
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={`${width} ${containerAlign} ${align} flex flex-col gap-4 md:gap-6`}>
|
||||
{heading && (
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDTextBlock: Block = {
|
||||
slug: 'fdText',
|
||||
@ -74,5 +75,6 @@ export const FDTextBlock: Block = {
|
||||
{ label: 'Full', value: 'full' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -42,6 +42,7 @@ export const FDUspChecklistBlockComponent: React.FC<FDUspChecklistBlockProps> =
|
||||
checkColor = 'navy',
|
||||
sectionBackground = 'white',
|
||||
textColor = 'navy',
|
||||
anchorId,
|
||||
}) => {
|
||||
const bg = bgMap[sectionBackground || 'white']
|
||||
const headClr = headingMap[textColor || 'navy']
|
||||
@ -76,7 +77,7 @@ export const FDUspChecklistBlockComponent: React.FC<FDUspChecklistBlockProps> =
|
||||
) : null
|
||||
|
||||
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">
|
||||
{imagePosition === 'left' ? <>{imageContent}{textContent}</> : <>{textContent}{imageContent}</>}
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDUspChecklistBlock: Block = {
|
||||
slug: 'fdUspChecklist',
|
||||
@ -80,5 +81,6 @@ export const FDUspChecklistBlock: Block = {
|
||||
{ label: 'Vit', value: 'white' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -52,6 +52,7 @@ export const FDUspTableBlockComponent: React.FC<FDUspTableBlockProps> = ({
|
||||
checkColor = 'navy',
|
||||
sectionBackground = 'white',
|
||||
textColor = 'navy',
|
||||
anchorId,
|
||||
}) => {
|
||||
const bg = bgMap[sectionBackground || 'white']
|
||||
const headClr = headingMap[textColor || 'navy']
|
||||
@ -60,7 +61,7 @@ export const FDUspTableBlockComponent: React.FC<FDUspTableBlockProps> = ({
|
||||
const border = borderMap[textColor || 'navy']
|
||||
|
||||
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">
|
||||
{heading && (
|
||||
<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 { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDUspTableBlock: Block = {
|
||||
slug: 'fdUspTable',
|
||||
@ -70,5 +71,6 @@ export const FDUspTableBlock: Block = {
|
||||
{ label: 'Vit', value: 'white' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -58,6 +58,7 @@ export const FDVideoBlockComponent: React.FC<FDVideoBlockProps> = ({
|
||||
maxWidth = 'default',
|
||||
sectionBackground = 'white',
|
||||
textColor = 'auto',
|
||||
anchorId,
|
||||
}) => {
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
|
||||
@ -113,7 +114,7 @@ export const FDVideoBlockComponent: React.FC<FDVideoBlockProps> = ({
|
||||
)
|
||||
|
||||
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`}>
|
||||
|
||||
{(heading || description) && (
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDVideoBlock: Block = {
|
||||
slug: 'fdVideo',
|
||||
@ -145,5 +146,6 @@ export const FDVideoBlock: Block = {
|
||||
{ label: 'Vit', value: 'white' },
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -82,6 +82,7 @@ export const FDVpsCalculatorBlockComponent: React.FC<FDVpsCalculatorBlockProps>
|
||||
showAdminFee,
|
||||
adminFeeAmount,
|
||||
additionalServices = [],
|
||||
anchorId,
|
||||
}) => {
|
||||
const pricing = {
|
||||
windows: pricingWindowsLicense ?? DEFAULT_PRICING.windows,
|
||||
@ -153,7 +154,7 @@ export const FDVpsCalculatorBlockComponent: React.FC<FDVpsCalculatorBlockProps>
|
||||
const hasTillval = (additionalServices ?? []).length > 0
|
||||
|
||||
return (
|
||||
<section className={`fd-section ${bgClass}`}>
|
||||
<section id={anchorId || undefined} className={`fd-section ${bgClass}`}>
|
||||
<div className="fd-container">
|
||||
|
||||
{(heading || description) && (
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDVpsCalculatorBlock: Block = {
|
||||
slug: 'fdVpsCalculator',
|
||||
@ -125,5 +126,6 @@ export const FDVpsCalculatorBlock: Block = {
|
||||
},
|
||||
],
|
||||
},
|
||||
anchorField,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,7 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
||||
cardBackground = 'navy',
|
||||
buttonColor = 'yellow',
|
||||
sectionBackground = 'white',
|
||||
anchorId,
|
||||
}) => {
|
||||
const card = cardBgMap[cardBackground || 'navy']
|
||||
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
||||
@ -41,7 +42,7 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
||||
const hasImage = media && typeof media === 'object' && media.url
|
||||
|
||||
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={`${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">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Block } from 'payload'
|
||||
import { anchorField } from '@/fields/anchorField'
|
||||
|
||||
export const FDWideCardBlock: Block = {
|
||||
slug: 'fdWideCard',
|
||||
@ -75,5 +76,6 @@ export const FDWideCardBlock: Block = {
|
||||
{ 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
|
||||
*/
|
||||
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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdHero';
|
||||
@ -305,6 +309,10 @@ export interface FDCtaSideImageBlock {
|
||||
| null;
|
||||
imagePosition?: ('right' | 'left') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdCtaSideImage';
|
||||
@ -319,6 +327,10 @@ export interface FDFeatureAnnouncementBlock {
|
||||
ctaText?: string | null;
|
||||
ctaLink?: string | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdFeatureAnnouncement';
|
||||
@ -339,6 +351,10 @@ export interface FDServicesGridBlock {
|
||||
}[]
|
||||
| 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdServicesGrid';
|
||||
@ -360,6 +376,10 @@ export interface FDContactBlock {
|
||||
id?: string | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdContact';
|
||||
@ -392,6 +412,10 @@ export interface FDFaqBlock {
|
||||
}[]
|
||||
| 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdFaq';
|
||||
@ -430,6 +454,10 @@ export interface FDCardGridBlock {
|
||||
}[]
|
||||
| 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdCardGrid';
|
||||
@ -472,6 +500,10 @@ export interface FDPricingCardBlock {
|
||||
* Färg på blockrubriken
|
||||
*/
|
||||
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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdPricingCard';
|
||||
@ -525,6 +557,10 @@ export interface FDUspChecklistBlock {
|
||||
checkColor?: ('navy' | 'yellow' | 'gray') | null;
|
||||
sectionBackground?: ('white' | 'gray' | 'navy') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdUspChecklist';
|
||||
@ -542,6 +578,10 @@ export interface FDWideCardBlock {
|
||||
cardBackground?: ('navy' | 'yellow' | 'gray' | 'white') | null;
|
||||
buttonColor?: ('yellow' | 'navy' | 'white') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdWideCard';
|
||||
@ -567,6 +607,10 @@ export interface FDTechPropertiesBlock {
|
||||
sectionBackground?: ('navy' | 'white' | 'gray' | 'yellow') | null;
|
||||
categoryColor?: ('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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdTechProperties';
|
||||
@ -604,6 +648,10 @@ export interface FDUspTableBlock {
|
||||
checkColor?: ('navy' | 'yellow' | 'gray') | null;
|
||||
sectionBackground?: ('white' | 'gray' | 'navy') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdUspTable';
|
||||
@ -623,6 +671,10 @@ export interface FDHeaderTextImageBlock {
|
||||
textAlign?: ('left' | 'center') | null;
|
||||
sectionBackground?: ('white' | 'gray' | 'navy') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdHeaderTextImage';
|
||||
@ -662,6 +714,10 @@ export interface FDContactFormBlock {
|
||||
*/
|
||||
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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdContactForm';
|
||||
@ -881,6 +937,10 @@ export interface FDAlternateHeroBlock {
|
||||
image?: (number | null) | Media;
|
||||
imageCaption?: string | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdAlternateHero';
|
||||
@ -906,6 +966,10 @@ export interface FDStatisticsBlock {
|
||||
| null;
|
||||
sectionBackground?: ('white' | 'navy' | 'gray') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdStatistics';
|
||||
@ -996,6 +1060,10 @@ export interface FDServiceChooserBlock {
|
||||
}[]
|
||||
| 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdServiceChooser';
|
||||
@ -1050,6 +1118,10 @@ export interface FDDataTableBlock {
|
||||
* Gör den första kolumnen fet — användbart för namnkolumner.
|
||||
*/
|
||||
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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdDataTable';
|
||||
@ -1093,6 +1165,10 @@ export interface FDVpsCalculatorBlock {
|
||||
id?: string | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdVpsCalculator';
|
||||
@ -1179,6 +1255,10 @@ export interface FDServiceCalculatorBlock {
|
||||
* T.ex. "{percent}% rabatt på alla resurser". Använd {percent} som variabel.
|
||||
*/
|
||||
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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdServiceCalculator';
|
||||
@ -1230,6 +1310,10 @@ export interface FDTextBlock {
|
||||
textColor?: ('navy' | 'white' | 'yellow') | null;
|
||||
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdText';
|
||||
@ -1276,6 +1360,10 @@ export interface FDCodeEmbedBlock {
|
||||
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow' | 'transparent') | null;
|
||||
textColor?: ('auto' | 'navy' | 'white') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdCodeEmbed';
|
||||
@ -1313,6 +1401,10 @@ export interface FDVideoBlock {
|
||||
maxWidth?: ('default' | 'narrow' | 'wide') | null;
|
||||
sectionBackground?: ('white' | 'navy' | 'gray' | 'yellow' | 'transparent') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdVideo';
|
||||
@ -1334,6 +1426,10 @@ export interface FDCtaBannerBlock {
|
||||
sectionBackground?: ('yellow' | 'navy' | 'gray' | 'white') | null;
|
||||
alignment?: ('center' | 'left') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdCtaBanner';
|
||||
@ -1365,6 +1461,10 @@ export interface FDTestimonialBlock {
|
||||
| null;
|
||||
layout?: ('grid' | 'featured') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdTestimonial';
|
||||
@ -1396,6 +1496,10 @@ export interface FDTeamBlock {
|
||||
columns?: ('2' | '3' | '4') | null;
|
||||
cardStyle?: ('navy' | 'white' | 'gray') | 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;
|
||||
blockName?: string | null;
|
||||
blockType: 'fdTeam';
|
||||
@ -1808,6 +1912,7 @@ export interface FDHeroBlockSelect<T extends boolean = true> {
|
||||
overlayOpacity?: T;
|
||||
textColor?: T;
|
||||
theme?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -1824,6 +1929,7 @@ export interface FDCtaSideImageBlockSelect<T extends boolean = true> {
|
||||
imageOverlay?: T;
|
||||
imagePosition?: T;
|
||||
theme?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -1837,6 +1943,7 @@ export interface FDFeatureAnnouncementBlockSelect<T extends boolean = true> {
|
||||
ctaText?: T;
|
||||
ctaLink?: T;
|
||||
theme?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -1856,6 +1963,7 @@ export interface FDServicesGridBlockSelect<T extends boolean = true> {
|
||||
id?: T;
|
||||
};
|
||||
columns?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -1873,6 +1981,7 @@ export interface FDContactBlockSelect<T extends boolean = true> {
|
||||
link?: T;
|
||||
id?: T;
|
||||
};
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -1890,6 +1999,7 @@ export interface FDFaqBlockSelect<T extends boolean = true> {
|
||||
id?: T;
|
||||
};
|
||||
theme?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -1918,6 +2028,7 @@ export interface FDCardGridBlockSelect<T extends boolean = true> {
|
||||
id?: T;
|
||||
};
|
||||
sectionBackground?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -1947,6 +2058,7 @@ export interface FDPricingCardBlockSelect<T extends boolean = true> {
|
||||
buttonColor?: T;
|
||||
sectionBackground?: T;
|
||||
titleColor?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -1997,6 +2109,7 @@ export interface FDUspChecklistBlockSelect<T extends boolean = true> {
|
||||
checkColor?: T;
|
||||
sectionBackground?: T;
|
||||
textColor?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2013,6 +2126,7 @@ export interface FDWideCardBlockSelect<T extends boolean = true> {
|
||||
cardBackground?: T;
|
||||
buttonColor?: T;
|
||||
sectionBackground?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2031,6 +2145,7 @@ export interface FDTechPropertiesBlockSelect<T extends boolean = true> {
|
||||
sectionBackground?: T;
|
||||
categoryColor?: T;
|
||||
valueColor?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2050,6 +2165,7 @@ export interface FDUspTableBlockSelect<T extends boolean = true> {
|
||||
checkColor?: T;
|
||||
sectionBackground?: T;
|
||||
textColor?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2066,6 +2182,7 @@ export interface FDHeaderTextImageBlockSelect<T extends boolean = true> {
|
||||
textAlign?: T;
|
||||
sectionBackground?: T;
|
||||
textColor?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2091,6 +2208,7 @@ export interface FDContactFormBlockSelect<T extends boolean = true> {
|
||||
endpoint?: T;
|
||||
authToken?: T;
|
||||
};
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2131,6 +2249,7 @@ export interface FDAlternateHeroBlockSelect<T extends boolean = true> {
|
||||
image?: T;
|
||||
imageCaption?: T;
|
||||
sectionBackground?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2149,6 +2268,7 @@ export interface FDStatisticsBlockSelect<T extends boolean = true> {
|
||||
};
|
||||
sectionBackground?: T;
|
||||
numberColor?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2215,6 +2335,7 @@ export interface FDServiceChooserBlockSelect<T extends boolean = true> {
|
||||
id?: T;
|
||||
};
|
||||
sectionBackground?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2244,6 +2365,7 @@ export interface FDDataTableBlockSelect<T extends boolean = true> {
|
||||
stripeRows?: T;
|
||||
bordered?: T;
|
||||
firstColumnBold?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2274,6 +2396,7 @@ export interface FDVpsCalculatorBlockSelect<T extends boolean = true> {
|
||||
price?: T;
|
||||
id?: T;
|
||||
};
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2335,6 +2458,7 @@ export interface FDServiceCalculatorBlockSelect<T extends boolean = true> {
|
||||
};
|
||||
discountPercent?: T;
|
||||
discountLabel?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2370,6 +2494,7 @@ export interface FDTextBlockSelect<T extends boolean = true> {
|
||||
textColor?: T;
|
||||
sectionBackground?: T;
|
||||
maxWidth?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2391,6 +2516,7 @@ export interface FDCodeEmbedBlockSelect<T extends boolean = true> {
|
||||
sectionBackground?: T;
|
||||
textColor?: T;
|
||||
embedBackground?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2412,6 +2538,7 @@ export interface FDVideoBlockSelect<T extends boolean = true> {
|
||||
maxWidth?: T;
|
||||
sectionBackground?: T;
|
||||
textColor?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2429,6 +2556,7 @@ export interface FDCtaBannerBlockSelect<T extends boolean = true> {
|
||||
sectionBackground?: T;
|
||||
alignment?: T;
|
||||
size?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2450,6 +2578,7 @@ export interface FDTestimonialBlockSelect<T extends boolean = true> {
|
||||
};
|
||||
layout?: T;
|
||||
sectionBackground?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
@ -2474,6 +2603,7 @@ export interface FDTeamBlockSelect<T extends boolean = true> {
|
||||
columns?: T;
|
||||
cardStyle?: T;
|
||||
sectionBackground?: T;
|
||||
anchorId?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user