add image-and-text and image slider block types

This commit is contained in:
2024-05-15 06:33:14 +02:00
parent 71b0a87180
commit ba28578a0c
18 changed files with 453 additions and 47 deletions

70
dnscms/dnscms/blocks.py Normal file
View File

@ -0,0 +1,70 @@
from grapple.helpers import register_streamfield_block
from grapple.models import (
GraphQLImage,
GraphQLStreamfield,
GraphQLString,
)
from wagtail import blocks
from wagtail.images.blocks import ImageChooserBlock
@register_streamfield_block
class ImageWithTextBlock(blocks.StructBlock):
image = ImageChooserBlock(
label="Bilde",
)
image_format = blocks.ChoiceBlock(
choices=[
("fullwidth", "Fullbredde"),
("bleed", "Utfallende"),
("original", "Uendret størrelse"),
],
default="fullwidth",
icon="cup",
label="Bildeformat",
)
text = blocks.CharBlock(
label="Tekst",
required=False,
max_length=512,
)
graphql_fields = [
GraphQLImage("image", required=True),
GraphQLString("image_format", required=True),
GraphQLString("text"),
]
class Meta:
icon = "image"
@register_streamfield_block
class ImageSliderItemBlock(blocks.StructBlock):
image = ImageChooserBlock(
label="Bilde",
)
text = blocks.CharBlock(
label="Tekst",
required=False,
max_length=512,
)
graphql_fields = [GraphQLImage("image", required=True), GraphQLString("text")]
class Meta:
icon = "image"
@register_streamfield_block
class ImageSliderBlock(blocks.StructBlock):
images = blocks.ListBlock(
ImageSliderItemBlock(),
min_num=1,
label="Bilder",
)
graphql_fields = [GraphQLStreamfield("images", required=True)]
class Meta:
icon = "image"

View File

@ -0,0 +1,21 @@
# Generated by Django 5.0.6 on 2024-05-15 03:04
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('generic', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='genericpage',
name='body',
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('single_image_with_text', wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(label='Bilde')), ('text', wagtail.blocks.CharBlock(label='Tekst', max_length=512))]))]),
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 5.0.6 on 2024-05-15 03:11
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('generic', '0002_alter_genericpage_body'),
]
operations = [
migrations.AlterField(
model_name='genericpage',
name='body',
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock(label='Rik tekst')), ('image', wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(label='Bilde')), ('text', wagtail.blocks.CharBlock(label='Tekst', max_length=512))], label='Bilde'))]),
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 5.0.6 on 2024-05-15 04:11
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('generic', '0003_alter_genericpage_body'),
]
operations = [
migrations.AlterField(
model_name='genericpage',
name='body',
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock(label='Rik tekst')), ('image', wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(label='Bilde')), ('image_format', wagtail.blocks.ChoiceBlock(choices=[('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], icon='cup', label='Bildeformat')), ('text', wagtail.blocks.CharBlock(label='Tekst', max_length=512, required=False))], label='Bilde')), ('image_slider', wagtail.blocks.StructBlock([('images', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(label='Bilde')), ('text', wagtail.blocks.CharBlock(label='Tekst', max_length=512, required=False))]), label='Bilder', min_num=1))], label='Bildegalleri'))]),
),
]

View File

@ -2,9 +2,10 @@ from grapple.models import GraphQLStreamfield
from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.fields import StreamField
from wagtail.images.blocks import ImageChooserBlock
from wagtail.models import Page
from dnscms.blocks import ImageSliderBlock, ImageWithTextBlock
class GenericPage(Page):
subpage_types = ["generic.GenericPage"]
@ -12,13 +13,14 @@ class GenericPage(Page):
body = StreamField(
[
("paragraph", blocks.RichTextBlock()),
("image", ImageChooserBlock()),
("paragraph", blocks.RichTextBlock(label="Rik tekst")),
("image", ImageWithTextBlock(label="Bilde")),
("image_slider", ImageSliderBlock(label="Bildegalleri")),
]
)
content_panels = Page.content_panels + [
FieldPanel("body"),
FieldPanel("body", heading="Innhold"),
]
graphql_fields = [

View File

@ -13,13 +13,7 @@ const GenericFragmentDefinition = graphql(`
urlPath
title
body {
id
blockType
field
... on RichTextBlock {
rawValue
value
}
...Blocks
}
}
`);

View File

@ -40,17 +40,18 @@ export default async function Page({ params }: { params: { slug: string } }) {
});
const venue = (data?.venue ?? {}) as VenueFragment;
const featuredImage: any = venue.featuredImage
return (
<main className="site-main" id="main">
<section className="page-header">
<h1>{venue.title}</h1>
{venue.featuredImage && (
{featuredImage && (
<Image
src={venue.featuredImage.url}
alt={venue.featuredImage.alt}
width={venue.featuredImage.width}
height={venue.featuredImage.height}
src={featuredImage.url}
alt={featuredImage.alt}
width={featuredImage.width}
height={featuredImage.height}
sizes="100vw"
/>
)}

View File

@ -1,4 +1,6 @@
import { RichTextBlock } from "./RichTextBlock";
import { ImageWithTextBlock } from "./ImageWithTextBlock";
import { ImageSliderBlock } from "./ImageSliderBlock";
export const Blocks = ({ blocks }: any) => {
return blocks.map((block: any) => {
@ -6,6 +8,12 @@ export const Blocks = ({ blocks }: any) => {
case "RichTextBlock":
return <RichTextBlock block={block} />;
break;
case "ImageWithTextBlock":
return <ImageWithTextBlock block={block} />;
break;
case "ImageSliderBlock":
return <ImageSliderBlock block={block} />;
break;
default:
return <div>Unsupported block type {block.blockType}</div>;
console.log("unsupported block", block);

View File

@ -0,0 +1,21 @@
import { ImageSliderBlock as ImageSliderBlockType } from "@/gql/graphql";
import Image from "../general/Image";
import styles from "./imageSliderBlock.module.scss";
export const ImageSliderBlock = ({
block,
}: {
block: ImageSliderBlockType;
}) => {
return (
<div className={styles.imageSliderBlock}>
{block.images &&
block.images.map((imageItem: any) => (
<div key={imageItem.image.id}>
<div>{imageItem.image.url}</div>
<div>{imageItem.text}</div>
</div>
))}
</div>
);
};

View File

@ -0,0 +1,25 @@
import { ImageWithTextBlock as ImageWithTextBlockType } from "@/gql/graphql";
import Image from "../general/Image";
import styles from "./imageWithTextBlock.module.scss";
export const ImageWithTextBlock = ({
block,
}: {
block: ImageWithTextBlockType;
}) => {
return (
<div className={styles.imageWithTextBlock}>
<Image
src={block.image.url}
alt={block.image.alt ?? ""}
width={block.image.width}
height={block.image.height}
// width={0}
// height={0}
// sizes="20vw"
/>
<div>{block.imageFormat}</div>
{block.text && <div>{block.text}</div>}
</div>
);
};

View File

@ -5,6 +5,8 @@ import { Pig } from "../general/Pig";
import Link from "next/link";
export const EventHeader = ({ event }: { event: EventFragment }) => {
const featuredImage: any = event.featuredImage
return (
<div className={styles.eventHeader}>
<div className={styles.text}>
@ -61,12 +63,12 @@ export const EventHeader = ({ event }: { event: EventFragment }) => {
</div>
</div>
<div className={styles.image}>
{event.featuredImage && (
{featuredImage && (
<Image
src={event.featuredImage.url}
alt={event.featuredImage.alt}
width={event.featuredImage.width}
height={event.featuredImage.height}
src={featuredImage.url}
alt={featuredImage.alt}
width={featuredImage.width}
height={featuredImage.height}
sizes="100vw"
/>
)}

View File

@ -21,6 +21,7 @@ export const EventItem = ({
}) => {
const nextOccurrence = getClosestOccurrence(event);
const numOccurrences = event?.occurrences?.length ?? 0;
const featuredImage: any = event.featuredImage;
return (
<li
@ -29,10 +30,10 @@ export const EventItem = ({
} linkItem`}
>
<div className={styles.image}>
{event.featuredImage && (
{featuredImage && (
<Image
src={event.featuredImage.url}
alt={event.featuredImage.alt}
src={featuredImage.url}
alt={featuredImage.alt}
width={0}
height={0}
sizes="20vw"

View File

@ -4,13 +4,15 @@ import Link from "next/link";
import Image from "../general/Image";
export const VenueItem = ({ venue }: { venue: VenueFragment }) => {
const featuredImage: any = venue.featuredImage;
return (
<li className={`${styles.venueItem} linkItem`}>
<div className={styles.image}>
{venue.featuredImage && (
{featuredImage && (
<Image
src={venue.featuredImage.url}
alt={venue.featuredImage.alt}
src={featuredImage.url}
alt={featuredImage.alt}
width={0}
height={0}
sizes="20vw"

View File

@ -13,7 +13,7 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
* Therefore it is highly recommended to use the babel or swc plugin for production.
*/
const documents = {
"\n fragment Generic on GenericPage {\n __typename\n id\n urlPath\n title\n body {\n id\n blockType\n field\n ... on RichTextBlock {\n rawValue\n value\n }\n }\n }\n": types.GenericFragmentDoc,
"\n fragment Generic on GenericPage {\n __typename\n id\n urlPath\n title\n body {\n ...Blocks\n }\n }\n": types.GenericFragmentDoc,
"\n query allGenericSlugs {\n pages(contentType: \"generic.GenericPage\") {\n id\n urlPath\n }\n }\n ": types.AllGenericSlugsDocument,
"\n query genericPageByUrl($urlPath: String!) {\n page: page(contentType: \"generic.GenericPage\", urlPath: $urlPath) {\n ... on GenericPage {\n ...Generic\n }\n }\n }\n ": types.GenericPageByUrlDocument,
"\n query allEventSlugs {\n pages(contentType: \"events.EventPage\") {\n id\n slug\n }\n }\n ": types.AllEventSlugsDocument,
@ -29,7 +29,8 @@ const documents = {
"\n query allVenues {\n venues: pages(contentType: \"venues.VenuePage\") {\n ... on VenuePage {\n ...Venue\n }\n }\n }\n ": types.AllVenuesDocument,
"\n fragment Home on HomePage {\n ... on HomePage {\n featuredEvents {\n id\n }\n }\n }\n": types.HomeFragmentDoc,
"\n query home {\n events: pages(contentType: \"events.EventPage\") {\n ...Event\n }\n home: page(contentType: \"home.HomePage\", urlPath: \"/home/\") {\n ... on HomePage {\n ...Home\n }\n }\n }\n ": types.HomeDocument,
"\n fragment Image on CustomImage {\n url\n width\n height\n alt\n attribution\n }\n": types.ImageFragmentDoc,
"\n fragment Blocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n rawValue\n value\n }\n ... on ImageWithTextBlock {\n image {\n ...Image\n }\n imageFormat\n text\n }\n ... on ImageSliderBlock {\n images {\n ... on ImageSliderItemBlock {\n image {\n ...Image\n }\n text\n }\n }\n }\n }\n": types.BlocksFragmentDoc,
"\n fragment Image on CustomImage {\n id\n url\n width\n height\n alt\n attribution\n }\n": types.ImageFragmentDoc,
"\n fragment Event on EventPage {\n __typename\n id\n slug\n title\n body {\n id\n blockType\n field\n ... on RichTextBlock {\n rawValue\n value\n }\n }\n featuredImage {\n ...Image\n }\n facebookUrl\n ticketUrl\n priceRegular\n priceMember\n priceStudent\n categories {\n ... on EventCategory {\n name\n slug\n }\n }\n occurrences {\n ... on EventOccurrence {\n __typename\n id\n start\n end\n venue {\n __typename\n id\n slug\n title\n }\n }\n }\n }\n": types.EventFragmentDoc,
"\n query allEvents {\n events: pages(contentType: \"events.EventPage\") {\n ... on EventPage {\n ...Event\n }\n }\n eventCategories: eventCategories {\n ... on EventCategory {\n name\n slug\n showInFilters\n }\n }\n }\n": types.AllEventsDocument,
};
@ -51,7 +52,7 @@ export function graphql(source: string): unknown;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment Generic on GenericPage {\n __typename\n id\n urlPath\n title\n body {\n id\n blockType\n field\n ... on RichTextBlock {\n rawValue\n value\n }\n }\n }\n"): (typeof documents)["\n fragment Generic on GenericPage {\n __typename\n id\n urlPath\n title\n body {\n id\n blockType\n field\n ... on RichTextBlock {\n rawValue\n value\n }\n }\n }\n"];
export function graphql(source: "\n fragment Generic on GenericPage {\n __typename\n id\n urlPath\n title\n body {\n ...Blocks\n }\n }\n"): (typeof documents)["\n fragment Generic on GenericPage {\n __typename\n id\n urlPath\n title\n body {\n ...Blocks\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@ -115,7 +116,11 @@ export function graphql(source: "\n query home {\n events: pages(content
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment Image on CustomImage {\n url\n width\n height\n alt\n attribution\n }\n"): (typeof documents)["\n fragment Image on CustomImage {\n url\n width\n height\n alt\n attribution\n }\n"];
export function graphql(source: "\n fragment Blocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n rawValue\n value\n }\n ... on ImageWithTextBlock {\n image {\n ...Image\n }\n imageFormat\n text\n }\n ... on ImageSliderBlock {\n images {\n ... on ImageSliderItemBlock {\n image {\n ...Image\n }\n text\n }\n }\n }\n }\n"): (typeof documents)["\n fragment Blocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n rawValue\n value\n }\n ... on ImageWithTextBlock {\n image {\n ...Image\n }\n imageFormat\n text\n }\n ... on ImageSliderBlock {\n images {\n ... on ImageSliderItemBlock {\n image {\n ...Image\n }\n text\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment Image on CustomImage {\n id\n url\n width\n height\n alt\n attribution\n }\n"): (typeof documents)["\n fragment Image on CustomImage {\n id\n url\n width\n height\n alt\n attribution\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,37 @@
import { graphql } from "@/gql";
const BlockFragmentDefinition = graphql(`
fragment Blocks on StreamFieldInterface {
id
blockType
field
... on RichTextBlock {
rawValue
value
}
... on ImageWithTextBlock {
image {
...Image
}
imageFormat
text
}
... on ImageSliderBlock {
images {
... on ImageSliderItemBlock {
image {
...Image
}
text
}
}
}
}
`);
const ImageFragmentDefinition = graphql(`
fragment Image on CustomImage {
id
url
width
height