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

View File

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

View File

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

View File

@ -1,4 +1,6 @@
import { RichTextBlock } from "./RichTextBlock"; import { RichTextBlock } from "./RichTextBlock";
import { ImageWithTextBlock } from "./ImageWithTextBlock";
import { ImageSliderBlock } from "./ImageSliderBlock";
export const Blocks = ({ blocks }: any) => { export const Blocks = ({ blocks }: any) => {
return blocks.map((block: any) => { return blocks.map((block: any) => {
@ -6,6 +8,12 @@ export const Blocks = ({ blocks }: any) => {
case "RichTextBlock": case "RichTextBlock":
return <RichTextBlock block={block} />; return <RichTextBlock block={block} />;
break; break;
case "ImageWithTextBlock":
return <ImageWithTextBlock block={block} />;
break;
case "ImageSliderBlock":
return <ImageSliderBlock block={block} />;
break;
default: default:
return <div>Unsupported block type {block.blockType}</div>; return <div>Unsupported block type {block.blockType}</div>;
console.log("unsupported block", block); 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"; import Link from "next/link";
export const EventHeader = ({ event }: { event: EventFragment }) => { export const EventHeader = ({ event }: { event: EventFragment }) => {
const featuredImage: any = event.featuredImage
return ( return (
<div className={styles.eventHeader}> <div className={styles.eventHeader}>
<div className={styles.text}> <div className={styles.text}>
@ -61,12 +63,12 @@ export const EventHeader = ({ event }: { event: EventFragment }) => {
</div> </div>
</div> </div>
<div className={styles.image}> <div className={styles.image}>
{event.featuredImage && ( {featuredImage && (
<Image <Image
src={event.featuredImage.url} src={featuredImage.url}
alt={event.featuredImage.alt} alt={featuredImage.alt}
width={event.featuredImage.width} width={featuredImage.width}
height={event.featuredImage.height} height={featuredImage.height}
sizes="100vw" sizes="100vw"
/> />
)} )}

View File

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

View File

@ -4,13 +4,15 @@ import Link from "next/link";
import Image from "../general/Image"; import Image from "../general/Image";
export const VenueItem = ({ venue }: { venue: VenueFragment }) => { export const VenueItem = ({ venue }: { venue: VenueFragment }) => {
const featuredImage: any = venue.featuredImage;
return ( return (
<li className={`${styles.venueItem} linkItem`}> <li className={`${styles.venueItem} linkItem`}>
<div className={styles.image}> <div className={styles.image}>
{venue.featuredImage && ( {featuredImage && (
<Image <Image
src={venue.featuredImage.url} src={featuredImage.url}
alt={venue.featuredImage.alt} alt={featuredImage.alt}
width={0} width={0}
height={0} height={0}
sizes="20vw" 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. * Therefore it is highly recommended to use the babel or swc plugin for production.
*/ */
const documents = { 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 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 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, "\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 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 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 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 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, "\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. * 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. * 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. * 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. * 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"; 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(` const ImageFragmentDefinition = graphql(`
fragment Image on CustomImage { fragment Image on CustomImage {
id
url url
width width
height height