From 21e377839785a82b392999d0f6e3afa53e445b2f Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Tue, 24 Feb 2026 10:10:29 +0100 Subject: [PATCH] feat: anchor links, smooth scroll, back-to-top --- add-anchor-links.sh | 260 ++++++++++++++++++ src/app/(frontend)/globals.css | 6 + src/app/(frontend)/layout.tsx | 2 + src/blocks/FDAlternateHeroBlock/Component.tsx | 3 +- src/blocks/FDAlternateHeroBlock/config.ts | 2 + src/blocks/FDCardGridBlock/Component.tsx | 3 +- src/blocks/FDCardGridBlock/config.ts | 4 +- src/blocks/FDCodeEmbedBlock/Component.tsx | 3 +- src/blocks/FDCodeEmbedBlock/config.ts | 4 +- src/blocks/FDContactBlock/Component.tsx | 3 +- src/blocks/FDContactBlock/config.ts | 4 +- src/blocks/FDContactFormBlock/Component.tsx | 3 +- src/blocks/FDContactFormBlock/config.ts | 4 +- src/blocks/FDCtaBannerBlock/Component.tsx | 3 +- src/blocks/FDCtaBannerBlock/config.ts | 4 +- src/blocks/FDCtaSideImageBlock/Component.tsx | 3 +- src/blocks/FDCtaSideImageBlock/config.ts | 4 +- src/blocks/FDDataTableBlock/Component.tsx | 3 +- src/blocks/FDDataTableBlock/config.ts | 2 + src/blocks/FDFaqBlock/Component.tsx | 3 +- src/blocks/FDFaqBlock/config.ts | 4 +- .../FDFeatureAnnouncementBlock/Component.tsx | 3 +- .../FDFeatureAnnouncementBlock/config.ts | 4 +- .../FDHeaderTextImageBlock/Component.tsx | 3 +- src/blocks/FDHeaderTextImageBlock/config.ts | 4 +- src/blocks/FDHeroBlock/Component.tsx | 3 +- src/blocks/FDHeroBlock/config.ts | 2 + src/blocks/FDPricingCardBlock/Component.tsx | 3 +- src/blocks/FDPricingCardBlock/config.ts | 4 +- .../FDServiceCalculatorBlock/Component.tsx | 3 +- src/blocks/FDServiceCalculatorBlock/config.ts | 4 +- .../FDServiceChooserBlock/Component.tsx | 3 +- src/blocks/FDServiceChooserBlock/config.ts | 2 + src/blocks/FDServicesGridBlock/Component.tsx | 3 +- src/blocks/FDServicesGridBlock/config.ts | 4 +- src/blocks/FDStatisticsBlock/Component.tsx | 3 +- src/blocks/FDStatisticsBlock/config.ts | 2 + src/blocks/FDTeamBlock/Component.tsx | 3 +- src/blocks/FDTeamBlock/config.ts | 4 +- .../FDTechPropertiesBlock/Component.tsx | 3 +- src/blocks/FDTechPropertiesBlock/config.ts | 4 +- src/blocks/FDTestimonialBlock/Component.tsx | 3 +- src/blocks/FDTestimonialBlock/config.ts | 4 +- src/blocks/FDTextBlock/Component.tsx | 3 +- src/blocks/FDTextBlock/config.ts | 4 +- src/blocks/FDUspChecklistBlock/Component.tsx | 3 +- src/blocks/FDUspChecklistBlock/config.ts | 4 +- src/blocks/FDUspTableBlock/Component.tsx | 3 +- src/blocks/FDUspTableBlock/config.ts | 4 +- src/blocks/FDVideoBlock/Component.tsx | 3 +- src/blocks/FDVideoBlock/config.ts | 4 +- src/blocks/FDVpsCalculatorBlock/Component.tsx | 3 +- src/blocks/FDVpsCalculatorBlock/config.ts | 4 +- src/blocks/FDWideCardBlock/Component.tsx | 3 +- src/blocks/FDWideCardBlock/config.ts | 4 +- src/components/BackToTop/index.tsx | 47 ++++ src/fields/anchorField.ts | 29 ++ src/payload-types.ts | 130 +++++++++ tsconfig.tsbuildinfo | 2 +- 59 files changed, 600 insertions(+), 48 deletions(-) create mode 100644 add-anchor-links.sh create mode 100644 src/components/BackToTop/index.tsx create mode 100644 src/fields/anchorField.ts diff --git a/add-anchor-links.sh b/add-anchor-links.sh new file mode 100644 index 0000000..612ce25 --- /dev/null +++ b/add-anchor-links.sh @@ -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: + *
+ */ +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
/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 ( + + ) +} +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