diff --git a/src/blocks/ImageSliderBlock.ts b/src/blocks/ImageSliderBlock.ts new file mode 100644 index 0000000..30662a3 --- /dev/null +++ b/src/blocks/ImageSliderBlock.ts @@ -0,0 +1,549 @@ +import type { Block } from 'payload' + +/** + * ImageSliderBlock / GalleryBlock + * + * Vielseitiger Bild-Slider und Galerie-Block mit: + * - Mehreren Layouts (Slider, Grid, Masonry) + * - Lightbox-Funktion + * - Thumbnail-Navigation + * - Bildunterschriften + * - Verschiedenen Animationen + */ +export const ImageSliderBlock: Block = { + slug: 'image-slider-block', + labels: { + singular: 'Bildergalerie', + plural: 'Bildergalerien', + }, + imageURL: '/assets/blocks/image-slider.png', + fields: [ + // Titel & Beschreibung + { + name: 'title', + type: 'text', + label: 'Titel', + localized: true, + admin: { + description: 'Optionaler Titel über der Galerie', + }, + }, + { + name: 'description', + type: 'textarea', + label: 'Beschreibung', + localized: true, + }, + + // Bilder Array + { + name: 'images', + type: 'array', + required: true, + minRows: 1, + maxRows: 50, + label: 'Bilder', + labels: { + singular: 'Bild', + plural: 'Bilder', + }, + admin: { + description: 'Mindestens 1 Bild, maximal 50 Bilder', + initCollapsed: false, + }, + fields: [ + { + name: 'image', + type: 'upload', + relationTo: 'media', + required: true, + label: 'Bild', + }, + { + name: 'caption', + type: 'text', + label: 'Bildunterschrift', + localized: true, + }, + { + name: 'alt', + type: 'text', + label: 'Alt-Text', + localized: true, + admin: { + description: 'Alternativer Text für Barrierefreiheit (falls abweichend vom Media Alt)', + }, + }, + { + name: 'link', + type: 'text', + label: 'Link', + admin: { + description: 'Optionaler Link beim Klick (statt Lightbox)', + }, + }, + ], + }, + + // Layout + { + name: 'layout', + type: 'select', + defaultValue: 'slider', + label: 'Layout', + options: [ + { label: 'Slider', value: 'slider' }, + { label: 'Grid', value: 'grid' }, + { label: 'Masonry', value: 'masonry' }, + { label: 'Einzelbild (Featured)', value: 'single' }, + { label: 'Thumbnails unten', value: 'thumbs' }, + { label: 'Filmstreifen', value: 'filmstrip' }, + ], + }, + + // Grid/Masonry Optionen + { + name: 'grid', + type: 'group', + label: 'Grid-Einstellungen', + admin: { + condition: (_, siblingData) => + siblingData?.layout === 'grid' || siblingData?.layout === 'masonry', + }, + fields: [ + { + name: 'cols', + type: 'select', + defaultValue: '3', + label: 'Spalten', + options: [ + { label: '2 Spalten', value: '2' }, + { label: '3 Spalten', value: '3' }, + { label: '4 Spalten', value: '4' }, + { label: '5 Spalten', value: '5' }, + { label: '6 Spalten', value: '6' }, + ], + }, + { + name: 'gap', + type: 'select', + defaultValue: '16', + label: 'Abstand', + options: [ + { label: 'Kein Abstand', value: '0' }, + { label: 'Klein (8px)', value: '8' }, + { label: 'Normal (16px)', value: '16' }, + { label: 'Groß (24px)', value: '24' }, + { label: 'Sehr groß (32px)', value: '32' }, + ], + }, + { + name: 'aspectRatio', + type: 'select', + defaultValue: 'square', + label: 'Seitenverhältnis', + options: [ + { label: 'Quadratisch (1:1)', value: 'square' }, + { label: 'Querformat (4:3)', value: '4-3' }, + { label: 'Breit (16:9)', value: '16-9' }, + { label: 'Hochformat (3:4)', value: '3-4' }, + { label: 'Original', value: 'auto' }, + ], + admin: { + condition: (_, siblingData) => siblingData?.layout !== 'masonry', + }, + }, + ], + }, + + // Slider-Einstellungen + { + name: 'slider', + type: 'group', + label: 'Slider-Einstellungen', + admin: { + condition: (_, siblingData) => + siblingData?.layout === 'slider' || + siblingData?.layout === 'thumbs' || + siblingData?.layout === 'filmstrip', + }, + fields: [ + { + name: 'effect', + type: 'select', + defaultValue: 'slide', + label: 'Animation', + options: [ + { label: 'Schieben (Slide)', value: 'slide' }, + { label: 'Überblenden (Fade)', value: 'fade' }, + { label: 'Zoom', value: 'zoom' }, + { label: 'Flip', value: 'flip' }, + { label: 'Coverflow', value: 'coverflow' }, + { label: 'Cube', value: 'cube' }, + ], + }, + { + name: 'speed', + type: 'select', + defaultValue: '500', + label: 'Geschwindigkeit', + options: [ + { label: 'Schnell (300ms)', value: '300' }, + { label: 'Normal (500ms)', value: '500' }, + { label: 'Langsam (800ms)', value: '800' }, + ], + }, + { + name: 'perView', + type: 'select', + defaultValue: '1', + label: 'Sichtbare Bilder', + options: [ + { label: '1', value: '1' }, + { label: '2', value: '2' }, + { label: '3', value: '3' }, + { label: '4', value: '4' }, + { label: 'Auto', value: 'auto' }, + ], + }, + { + name: 'gap', + type: 'select', + defaultValue: '0', + label: 'Abstand', + options: [ + { label: 'Kein Abstand', value: '0' }, + { label: 'Klein (8px)', value: '8' }, + { label: 'Normal (16px)', value: '16' }, + { label: 'Groß (24px)', value: '24' }, + ], + }, + { + name: 'auto', + type: 'checkbox', + defaultValue: false, + label: 'Autoplay', + }, + { + name: 'delay', + type: 'select', + defaultValue: '4000', + label: 'Intervall', + options: [ + { label: '3 Sekunden', value: '3000' }, + { label: '4 Sekunden', value: '4000' }, + { label: '5 Sekunden', value: '5000' }, + { label: '6 Sekunden', value: '6000' }, + { label: '8 Sekunden', value: '8000' }, + ], + admin: { + condition: (_, siblingData) => siblingData?.auto, + }, + }, + { + name: 'pauseHover', + type: 'checkbox', + defaultValue: true, + label: 'Bei Hover pausieren', + admin: { + condition: (_, siblingData) => siblingData?.auto, + }, + }, + { + name: 'loop', + type: 'checkbox', + defaultValue: true, + label: 'Endlosschleife', + }, + { + name: 'centered', + type: 'checkbox', + defaultValue: false, + label: 'Aktives Bild zentrieren', + }, + ], + }, + + // Navigation + { + name: 'nav', + type: 'group', + label: 'Navigation', + admin: { + condition: (_, siblingData) => + siblingData?.layout === 'slider' || + siblingData?.layout === 'thumbs' || + siblingData?.layout === 'filmstrip', + }, + fields: [ + { + name: 'arrows', + type: 'checkbox', + defaultValue: true, + label: 'Pfeile anzeigen', + }, + { + name: 'arrowStyle', + type: 'select', + defaultValue: 'default', + label: 'Pfeil-Stil', + options: [ + { label: 'Standard', value: 'default' }, + { label: 'Minimal', value: 'minimal' }, + { label: 'Eckig', value: 'square' }, + { label: 'Nur bei Hover', value: 'hover' }, + ], + admin: { + condition: (_, siblingData) => siblingData?.arrows, + }, + }, + { + name: 'dots', + type: 'checkbox', + defaultValue: true, + label: 'Punkte anzeigen', + }, + { + name: 'dotStyle', + type: 'select', + defaultValue: 'dots', + label: 'Punkte-Stil', + options: [ + { label: 'Punkte', value: 'dots' }, + { label: 'Striche', value: 'lines' }, + { label: 'Nummern', value: 'numbers' }, + { label: 'Fortschritt', value: 'progress' }, + ], + admin: { + condition: (_, siblingData) => siblingData?.dots, + }, + }, + { + name: 'counter', + type: 'checkbox', + defaultValue: false, + label: 'Zähler anzeigen (1/10)', + }, + { + name: 'swipe', + type: 'checkbox', + defaultValue: true, + label: 'Touch-Swipe', + }, + { + name: 'keys', + type: 'checkbox', + defaultValue: true, + label: 'Tastaturnavigation', + }, + ], + }, + + // Lightbox + { + name: 'lightbox', + type: 'group', + label: 'Lightbox', + fields: [ + { + name: 'enabled', + type: 'checkbox', + defaultValue: true, + label: 'Lightbox aktivieren', + admin: { + description: 'Bilder bei Klick in Vollansicht öffnen', + }, + }, + { + name: 'zoom', + type: 'checkbox', + defaultValue: true, + label: 'Zoom erlauben', + admin: { + condition: (_, siblingData) => siblingData?.enabled, + }, + }, + { + name: 'download', + type: 'checkbox', + defaultValue: false, + label: 'Download-Button', + admin: { + condition: (_, siblingData) => siblingData?.enabled, + }, + }, + { + name: 'share', + type: 'checkbox', + defaultValue: false, + label: 'Teilen-Button', + admin: { + condition: (_, siblingData) => siblingData?.enabled, + }, + }, + { + name: 'captions', + type: 'checkbox', + defaultValue: true, + label: 'Bildunterschriften anzeigen', + admin: { + condition: (_, siblingData) => siblingData?.enabled, + }, + }, + { + name: 'thumbs', + type: 'checkbox', + defaultValue: true, + label: 'Thumbnails anzeigen', + admin: { + condition: (_, siblingData) => siblingData?.enabled, + }, + }, + { + name: 'bg', + type: 'select', + defaultValue: 'dark', + label: 'Hintergrund', + options: [ + { label: 'Dunkel', value: 'dark' }, + { label: 'Hell', value: 'light' }, + { label: 'Blur', value: 'blur' }, + ], + admin: { + condition: (_, siblingData) => siblingData?.enabled, + }, + }, + ], + }, + + // Darstellung + { + name: 'style', + type: 'group', + label: 'Darstellung', + fields: [ + { + name: 'height', + type: 'select', + defaultValue: 'auto', + label: 'Höhe', + options: [ + { label: 'Auto (Bildverhältnis)', value: 'auto' }, + { label: 'Klein (300px)', value: '300' }, + { label: 'Mittel (400px)', value: '400' }, + { label: 'Groß (500px)', value: '500' }, + { label: 'Sehr groß (600px)', value: '600' }, + { label: 'Vollbild (100vh)', value: 'full' }, + ], + }, + { + name: 'imgFit', + type: 'select', + defaultValue: 'cover', + label: 'Bildanpassung', + options: [ + { label: 'Ausfüllen (cover)', value: 'cover' }, + { label: 'Einpassen (contain)', value: 'contain' }, + { label: 'Strecken (fill)', value: 'fill' }, + ], + }, + { + name: 'rounded', + type: 'select', + defaultValue: 'none', + label: 'Ecken', + options: [ + { label: 'Keine', value: 'none' }, + { label: 'Klein', value: 'sm' }, + { label: 'Mittel', value: 'md' }, + { label: 'Groß', value: 'lg' }, + { label: 'Rund', value: 'full' }, + ], + }, + { + name: 'shadow', + type: 'checkbox', + defaultValue: false, + label: 'Schatten', + }, + { + name: 'border', + type: 'checkbox', + defaultValue: false, + label: 'Rahmen', + }, + { + name: 'hoverEffect', + type: 'select', + defaultValue: 'none', + label: 'Hover-Effekt', + options: [ + { label: 'Keiner', value: 'none' }, + { label: 'Zoom', value: 'zoom' }, + { label: 'Aufhellen', value: 'brighten' }, + { label: 'Abdunkeln', value: 'darken' }, + { label: 'Overlay', value: 'overlay' }, + ], + }, + { + name: 'captionPos', + type: 'select', + defaultValue: 'below', + label: 'Bildunterschrift-Position', + options: [ + { label: 'Unterhalb', value: 'below' }, + { label: 'Overlay unten', value: 'overlay' }, + { label: 'Bei Hover', value: 'hover' }, + { label: 'Versteckt', value: 'hidden' }, + ], + }, + { + name: 'bg', + type: 'select', + defaultValue: 'none', + label: 'Hintergrund', + options: [ + { label: 'Keiner', value: 'none' }, + { label: 'Weiß', value: 'white' }, + { label: 'Grau', value: 'light' }, + { label: 'Dunkel', value: 'dark' }, + ], + }, + { + name: 'padding', + type: 'select', + defaultValue: 'none', + label: 'Außenabstand', + options: [ + { label: 'Keiner', value: 'none' }, + { label: 'Klein', value: 'sm' }, + { label: 'Normal', value: 'md' }, + { label: 'Groß', value: 'lg' }, + ], + }, + ], + }, + + // Accessibility + { + name: 'a11y', + type: 'group', + label: 'Barrierefreiheit', + fields: [ + { + name: 'label', + type: 'text', + defaultValue: 'Bildergalerie', + label: 'ARIA Label', + localized: true, + }, + { + name: 'reducedMotion', + type: 'checkbox', + defaultValue: true, + label: 'Reduzierte Bewegung respektieren', + }, + ], + }, + ], +} diff --git a/src/blocks/index.ts b/src/blocks/index.ts index ec5e0d9..9e3017a 100644 --- a/src/blocks/index.ts +++ b/src/blocks/index.ts @@ -1,5 +1,6 @@ export { HeroBlock } from './HeroBlock' export { HeroSliderBlock } from './HeroSliderBlock' +export { ImageSliderBlock } from './ImageSliderBlock' export { TextBlock } from './TextBlock' export { ImageTextBlock } from './ImageTextBlock' export { CardGridBlock } from './CardGridBlock' diff --git a/src/collections/Pages.ts b/src/collections/Pages.ts index e4be575..97d1083 100644 --- a/src/collections/Pages.ts +++ b/src/collections/Pages.ts @@ -2,6 +2,7 @@ import type { CollectionConfig } from 'payload' import { HeroBlock, HeroSliderBlock, + ImageSliderBlock, TextBlock, ImageTextBlock, CardGridBlock, @@ -75,6 +76,7 @@ export const Pages: CollectionConfig = { // Bestehende Blocks HeroBlock, HeroSliderBlock, + ImageSliderBlock, TextBlock, ImageTextBlock, CardGridBlock, diff --git a/src/migrations/20251213_210000_image_slider_block.ts b/src/migrations/20251213_210000_image_slider_block.ts new file mode 100644 index 0000000..9886ee7 --- /dev/null +++ b/src/migrations/20251213_210000_image_slider_block.ts @@ -0,0 +1,224 @@ +import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres' + +/** + * Migration: ImageSliderBlock + * + * Erstellt den ImageSliderBlock für Bildergalerien mit: + * - Verschiedenen Layouts (Slider, Grid, Masonry) + * - Lightbox-Funktion + * - Flexible Slider-Optionen + * - Styling und Navigation + */ + +export async function up({ db }: MigrateUpArgs): Promise { + // Create enums + await db.execute(sql` + -- Layout + CREATE TYPE "enum_pages_blocks_image_slider_block_layout" AS ENUM('slider', 'grid', 'masonry', 'single', 'thumbs', 'filmstrip'); + + -- Grid + CREATE TYPE "enum_pages_blocks_image_slider_block_grid_cols" AS ENUM('2', '3', '4', '5', '6'); + CREATE TYPE "enum_pages_blocks_image_slider_block_grid_gap" AS ENUM('0', '8', '16', '24', '32'); + CREATE TYPE "enum_pages_blocks_image_slider_block_grid_aspect" AS ENUM('square', '4-3', '16-9', '3-4', 'auto'); + + -- Slider + CREATE TYPE "enum_pages_blocks_image_slider_block_slider_effect" AS ENUM('slide', 'fade', 'zoom', 'flip', 'coverflow', 'cube'); + CREATE TYPE "enum_pages_blocks_image_slider_block_slider_speed" AS ENUM('300', '500', '800'); + CREATE TYPE "enum_pages_blocks_image_slider_block_slider_per_view" AS ENUM('1', '2', '3', '4', 'auto'); + CREATE TYPE "enum_pages_blocks_image_slider_block_slider_gap" AS ENUM('0', '8', '16', '24'); + CREATE TYPE "enum_pages_blocks_image_slider_block_slider_delay" AS ENUM('3000', '4000', '5000', '6000', '8000'); + + -- Navigation + CREATE TYPE "enum_pages_blocks_image_slider_block_nav_arrow_style" AS ENUM('default', 'minimal', 'square', 'hover'); + CREATE TYPE "enum_pages_blocks_image_slider_block_nav_dot_style" AS ENUM('dots', 'lines', 'numbers', 'progress'); + + -- Lightbox + CREATE TYPE "enum_pages_blocks_image_slider_block_lightbox_bg" AS ENUM('dark', 'light', 'blur'); + + -- Style + CREATE TYPE "enum_pages_blocks_image_slider_block_style_height" AS ENUM('auto', '300', '400', '500', '600', 'full'); + CREATE TYPE "enum_pages_blocks_image_slider_block_style_img_fit" AS ENUM('cover', 'contain', 'fill'); + CREATE TYPE "enum_pages_blocks_image_slider_block_style_rounded" AS ENUM('none', 'sm', 'md', 'lg', 'full'); + CREATE TYPE "enum_pages_blocks_image_slider_block_style_hover" AS ENUM('none', 'zoom', 'brighten', 'darken', 'overlay'); + CREATE TYPE "enum_pages_blocks_image_slider_block_style_caption" AS ENUM('below', 'overlay', 'hover', 'hidden'); + CREATE TYPE "enum_pages_blocks_image_slider_block_style_bg" AS ENUM('none', 'white', 'light', 'dark'); + CREATE TYPE "enum_pages_blocks_image_slider_block_style_padding" AS ENUM('none', 'sm', 'md', 'lg'); + `) + + // Create images array table + await db.execute(sql` + CREATE TABLE "pages_blocks_image_slider_block_images" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "image_id" integer NOT NULL, + "link" varchar + ); + `) + + // Create images locales table + await db.execute(sql` + CREATE TABLE "pages_blocks_image_slider_block_images_locales" ( + "caption" varchar, + "alt" varchar, + "id" serial PRIMARY KEY NOT NULL, + "_locale" "_locales" NOT NULL, + "_parent_id" varchar NOT NULL + ); + `) + + // Create main block table + await db.execute(sql` + CREATE TABLE "pages_blocks_image_slider_block" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + + -- Layout + "layout" "enum_pages_blocks_image_slider_block_layout" DEFAULT 'slider', + + -- Grid settings + "grid_cols" "enum_pages_blocks_image_slider_block_grid_cols" DEFAULT '3', + "grid_gap" "enum_pages_blocks_image_slider_block_grid_gap" DEFAULT '16', + "grid_aspect_ratio" "enum_pages_blocks_image_slider_block_grid_aspect" DEFAULT 'square', + + -- Slider settings + "slider_effect" "enum_pages_blocks_image_slider_block_slider_effect" DEFAULT 'slide', + "slider_speed" "enum_pages_blocks_image_slider_block_slider_speed" DEFAULT '500', + "slider_per_view" "enum_pages_blocks_image_slider_block_slider_per_view" DEFAULT '1', + "slider_gap" "enum_pages_blocks_image_slider_block_slider_gap" DEFAULT '0', + "slider_auto" boolean DEFAULT false, + "slider_delay" "enum_pages_blocks_image_slider_block_slider_delay" DEFAULT '4000', + "slider_pause_hover" boolean DEFAULT true, + "slider_loop" boolean DEFAULT true, + "slider_centered" boolean DEFAULT false, + + -- Navigation + "nav_arrows" boolean DEFAULT true, + "nav_arrow_style" "enum_pages_blocks_image_slider_block_nav_arrow_style" DEFAULT 'default', + "nav_dots" boolean DEFAULT true, + "nav_dot_style" "enum_pages_blocks_image_slider_block_nav_dot_style" DEFAULT 'dots', + "nav_counter" boolean DEFAULT false, + "nav_swipe" boolean DEFAULT true, + "nav_keys" boolean DEFAULT true, + + -- Lightbox + "lightbox_enabled" boolean DEFAULT true, + "lightbox_zoom" boolean DEFAULT true, + "lightbox_download" boolean DEFAULT false, + "lightbox_share" boolean DEFAULT false, + "lightbox_captions" boolean DEFAULT true, + "lightbox_thumbs" boolean DEFAULT true, + "lightbox_bg" "enum_pages_blocks_image_slider_block_lightbox_bg" DEFAULT 'dark', + + -- Style + "style_height" "enum_pages_blocks_image_slider_block_style_height" DEFAULT 'auto', + "style_img_fit" "enum_pages_blocks_image_slider_block_style_img_fit" DEFAULT 'cover', + "style_rounded" "enum_pages_blocks_image_slider_block_style_rounded" DEFAULT 'none', + "style_shadow" boolean DEFAULT false, + "style_border" boolean DEFAULT false, + "style_hover_effect" "enum_pages_blocks_image_slider_block_style_hover" DEFAULT 'none', + "style_caption_pos" "enum_pages_blocks_image_slider_block_style_caption" DEFAULT 'below', + "style_bg" "enum_pages_blocks_image_slider_block_style_bg" DEFAULT 'none', + "style_padding" "enum_pages_blocks_image_slider_block_style_padding" DEFAULT 'none', + + -- Accessibility + "a11y_reduced_motion" boolean DEFAULT true, + + "block_name" varchar + ); + `) + + // Create locales table + await db.execute(sql` + CREATE TABLE "pages_blocks_image_slider_block_locales" ( + "title" varchar, + "description" varchar, + "a11y_label" varchar DEFAULT 'Bildergalerie', + "id" serial PRIMARY KEY NOT NULL, + "_locale" "_locales" NOT NULL, + "_parent_id" varchar NOT NULL + ); + `) + + // Add foreign keys and indexes + await db.execute(sql` + -- Images table + ALTER TABLE "pages_blocks_image_slider_block_images" + ADD CONSTRAINT "pages_blocks_image_slider_block_images_image_id_fk" + FOREIGN KEY ("image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action; + + ALTER TABLE "pages_blocks_image_slider_block_images" + ADD CONSTRAINT "pages_blocks_image_slider_block_images_parent_id_fk" + FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_image_slider_block"("id") ON DELETE cascade ON UPDATE no action; + + CREATE INDEX "pages_blocks_image_slider_block_images_order_idx" + ON "pages_blocks_image_slider_block_images" USING btree ("_order"); + CREATE INDEX "pages_blocks_image_slider_block_images_parent_id_idx" + ON "pages_blocks_image_slider_block_images" USING btree ("_parent_id"); + CREATE INDEX "pages_blocks_image_slider_block_images_image_idx" + ON "pages_blocks_image_slider_block_images" USING btree ("image_id"); + + -- Images locales table + ALTER TABLE "pages_blocks_image_slider_block_images_locales" + ADD CONSTRAINT "pages_blocks_image_slider_block_images_locales_parent_fk" + FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_image_slider_block_images"("id") ON DELETE cascade ON UPDATE no action; + + CREATE UNIQUE INDEX "pages_blocks_image_slider_block_images_locales_uniq" + ON "pages_blocks_image_slider_block_images_locales" USING btree ("_locale", "_parent_id"); + + -- Main block table + ALTER TABLE "pages_blocks_image_slider_block" + ADD CONSTRAINT "pages_blocks_image_slider_block_parent_id_fk" + FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; + + CREATE INDEX "pages_blocks_image_slider_block_order_idx" + ON "pages_blocks_image_slider_block" USING btree ("_order"); + CREATE INDEX "pages_blocks_image_slider_block_parent_id_idx" + ON "pages_blocks_image_slider_block" USING btree ("_parent_id"); + CREATE INDEX "pages_blocks_image_slider_block_path_idx" + ON "pages_blocks_image_slider_block" USING btree ("_path"); + + -- Locales table + ALTER TABLE "pages_blocks_image_slider_block_locales" + ADD CONSTRAINT "pages_blocks_image_slider_block_locales_parent_id_fk" + FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_image_slider_block"("id") ON DELETE cascade ON UPDATE no action; + + CREATE UNIQUE INDEX "pages_blocks_image_slider_block_locales_locale_parent_uniq" + ON "pages_blocks_image_slider_block_locales" USING btree ("_locale", "_parent_id"); + `) +} + +export async function down({ db }: MigrateDownArgs): Promise { + // Drop tables + await db.execute(sql` + DROP TABLE IF EXISTS "pages_blocks_image_slider_block_images_locales" CASCADE; + DROP TABLE IF EXISTS "pages_blocks_image_slider_block_images" CASCADE; + DROP TABLE IF EXISTS "pages_blocks_image_slider_block_locales" CASCADE; + DROP TABLE IF EXISTS "pages_blocks_image_slider_block" CASCADE; + `) + + // Drop enums + await db.execute(sql` + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_layout"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_grid_cols"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_grid_gap"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_grid_aspect"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_slider_effect"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_slider_speed"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_slider_per_view"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_slider_gap"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_slider_delay"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_nav_arrow_style"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_nav_dot_style"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_lightbox_bg"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_style_height"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_style_img_fit"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_style_rounded"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_style_hover"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_style_caption"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_style_bg"; + DROP TYPE IF EXISTS "enum_pages_blocks_image_slider_block_style_padding"; + `) +} diff --git a/src/payload-types.ts b/src/payload-types.ts index 6a308a6..47026d5 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -536,6 +536,85 @@ export interface Page { blockName?: string | null; blockType: 'hero-slider-block'; } + | { + /** + * Optionaler Titel über der Galerie + */ + title?: string | null; + description?: string | null; + /** + * Mindestens 1 Bild, maximal 50 Bilder + */ + images: { + image: number | Media; + caption?: string | null; + /** + * Alternativer Text für Barrierefreiheit (falls abweichend vom Media Alt) + */ + alt?: string | null; + /** + * Optionaler Link beim Klick (statt Lightbox) + */ + link?: string | null; + id?: string | null; + }[]; + layout?: ('slider' | 'grid' | 'masonry' | 'single' | 'thumbs' | 'filmstrip') | null; + grid?: { + cols?: ('2' | '3' | '4' | '5' | '6') | null; + gap?: ('0' | '8' | '16' | '24' | '32') | null; + aspectRatio?: ('square' | '4-3' | '16-9' | '3-4' | 'auto') | null; + }; + slider?: { + effect?: ('slide' | 'fade' | 'zoom' | 'flip' | 'coverflow' | 'cube') | null; + speed?: ('300' | '500' | '800') | null; + perView?: ('1' | '2' | '3' | '4' | 'auto') | null; + gap?: ('0' | '8' | '16' | '24') | null; + auto?: boolean | null; + delay?: ('3000' | '4000' | '5000' | '6000' | '8000') | null; + pauseHover?: boolean | null; + loop?: boolean | null; + centered?: boolean | null; + }; + nav?: { + arrows?: boolean | null; + arrowStyle?: ('default' | 'minimal' | 'square' | 'hover') | null; + dots?: boolean | null; + dotStyle?: ('dots' | 'lines' | 'numbers' | 'progress') | null; + counter?: boolean | null; + swipe?: boolean | null; + keys?: boolean | null; + }; + lightbox?: { + /** + * Bilder bei Klick in Vollansicht öffnen + */ + enabled?: boolean | null; + zoom?: boolean | null; + download?: boolean | null; + share?: boolean | null; + captions?: boolean | null; + thumbs?: boolean | null; + bg?: ('dark' | 'light' | 'blur') | null; + }; + style?: { + height?: ('auto' | '300' | '400' | '500' | '600' | 'full') | null; + imgFit?: ('cover' | 'contain' | 'fill') | null; + rounded?: ('none' | 'sm' | 'md' | 'lg' | 'full') | null; + shadow?: boolean | null; + border?: boolean | null; + hoverEffect?: ('none' | 'zoom' | 'brighten' | 'darken' | 'overlay') | null; + captionPos?: ('below' | 'overlay' | 'hover' | 'hidden') | null; + bg?: ('none' | 'white' | 'light' | 'dark') | null; + padding?: ('none' | 'sm' | 'md' | 'lg') | null; + }; + a11y?: { + label?: string | null; + reducedMotion?: boolean | null; + }; + id?: string | null; + blockName?: string | null; + blockType: 'image-slider-block'; + } | { content: { root: { @@ -3419,6 +3498,85 @@ export interface PagesSelect { id?: T; blockName?: T; }; + 'image-slider-block'?: + | T + | { + title?: T; + description?: T; + images?: + | T + | { + image?: T; + caption?: T; + alt?: T; + link?: T; + id?: T; + }; + layout?: T; + grid?: + | T + | { + cols?: T; + gap?: T; + aspectRatio?: T; + }; + slider?: + | T + | { + effect?: T; + speed?: T; + perView?: T; + gap?: T; + auto?: T; + delay?: T; + pauseHover?: T; + loop?: T; + centered?: T; + }; + nav?: + | T + | { + arrows?: T; + arrowStyle?: T; + dots?: T; + dotStyle?: T; + counter?: T; + swipe?: T; + keys?: T; + }; + lightbox?: + | T + | { + enabled?: T; + zoom?: T; + download?: T; + share?: T; + captions?: T; + thumbs?: T; + bg?: T; + }; + style?: + | T + | { + height?: T; + imgFit?: T; + rounded?: T; + shadow?: T; + border?: T; + hoverEffect?: T; + captionPos?: T; + bg?: T; + padding?: T; + }; + a11y?: + | T + | { + label?: T; + reducedMotion?: T; + }; + id?: T; + blockName?: T; + }; 'text-block'?: | T | {