blökkli In-Page Editor
blökkli has a lot of configuration options. Please check the official documentation for more information.
blökkli comes with an adapter supporting Drupal paragraphs out of the box.
Nuxt Configuration
The blökkli starterkit comes with a pre-configured blökkli installation. blökkli
can be configured in the .config/blokkli.ts
file.
See the inline comments for more information on the configuration options.
import drupal from '@blokkli/editor/drupal'
import { defineNuxtConfigProperty } from '.'
export default defineNuxtConfigProperty<'blokkli'>((ctx) => {
return {
modules: [drupal()],
globalOptions: {
teaserStyle: {
type: 'radios',
label: 'Display',
default: 'grid',
options: {
grid: 'Grid',
staggered_grid: 'Staggered Grid',
},
},
mobileStyle: {
type: 'radios',
label: 'Mobile display',
default: 'stapel',
options: {
stack: 'Stack',
slider: 'Slider',
},
},
spacing: {
type: 'radios',
label: 'Spacing',
default: 'none',
displayAs: 'icons',
options: {
none: { label: 'no spacing', icon: 'icon-blokkli-option-no-spacing' },
small: {
label: 'small spacing',
icon: 'icon-blokkli-option-small-spacing',
},
large: {
label: 'large spacing',
icon: 'icon-blokkli-option-large-spacing',
},
},
},
imageFormat: {
type: 'radios',
label: 'Image Format',
default: 'full',
displayAs: 'icons',
options: {
full: { label: 'extra large', icon: 'icon-blokkli-option-xlarge' },
big: { label: 'large', icon: 'icon-blokkli-option-large' },
text: { label: 'medium', icon: 'icon-blokkli-option-medium' },
small: { label: 'small', icon: 'icon-blokkli-option-small' },
},
},
},
translations: {
en: {
editIndicatorLabel: 'Edit paragraphs',
},
de: {
editIndicatorLabel: 'Abschnitte bearbeiten',
},
},
chunkNames: ['global', 'rare'],
storageDefaults: {
blockFavorites: ['text'],
},
schemaOptionsPath: ctx.DEV
? '../../drupal/docroot/modules/custom/blokkli_starterkit/data/schema.json'
: undefined,
defaultLanguage: 'de',
// Make sure the editor is always rendered in German instead of the
// current page language.
forceDefaultLanguage: true,
}
})
Defining component options
blökkli allows you to define custom options per component. These options are saved as behaviour settings on the paragraph entity.
Common options are:
Checkbox
: This is the simplest option. It renders a single checkbox with the given label.Checkboxes
: This will render multiple checkboxes in a dropdown where zero or multiple options can be checked.Text
: This option renders a single text input.Color
: This option type renders a HTML color input.Radios
: This option renders a group of radio buttons.Range
: Renders a HTML range input to select a single numeric valueNumber
: Renders a HTML number input to enter an integer number.Grouping
: This option type allows you to group multiple options together.
Drupal Paragraphs integration
blökkli uses so called "adapters" to add support for various backends. The nuxt module ships with an adapter to integrate blökkli with the paragraphs_blokkli Drupal module.
The adapter is defined using the ./app/blokkli.editAdapter.ts
file. The file is optional if the Drupal backend is
used. You can extend/override any adapter method to add custom
functionality:
import { defineBlokkliEditAdapter } from '#blokkli/adapter'
import drupalAdapter from '#blokkli/drupal/adapter'
export default defineBlokkliEditAdapter(async (ctx) => {
const baseAdapter = await drupalAdapter(ctx)
const runtimeConfig = useRuntimeConfig()
return {
...baseAdapter,
// Override specific methods from the base adapter.
buildEditableFrameUrl(e) {
const url = baseAdapter.buildEditableFrameUrl?.(e)
if (!url) {
throw new Error('Failed to build editable frame URL')
}
return `${runtimeConfig.public.drupalBackendUrl}${url}`
},
formFrameBuilder(e) {
const result = baseAdapter.formFrameBuilder?.(e)
if (!result || !result.url) {
throw new Error('Failed to build form frame')
}
return {
url: `${runtimeConfig.public.drupalBackendUrl}${result.url}`,
}
},
}
})
Editable fields
For text and string fields in Drupal, you can annotate a DOM element to be "editable" using the machine name of the field. For example:
<template>
<div class="text-lg lg:text-xl">
<h2 v-blokkli-editable:field_title v-html="text" />
</div>
</template>
Assuming the paragraph has a field called field_title
, by using the
v-blokkli-editable
directive, you can tell blökkli what field in Drupal this
maps to.
Doing so allows editors to double click on the text and get an input field where they can directly edit the text.
blökkli automatically loads the field configuration to check for settings such
as required
or max length
.
Droppable media field integration
Similar to editable fields, you can mark DOM elements as "droppable". Doing so allows editors to replace referenced entities such as media images.
Assuming we have a paragraph with a field called field_image
that is an
entity_reference
field type, you could annotate it like this:
<template>
<div>
<div v-blokkli-droppable:field_image>
<MediaImage v-bind="image" />
</div>
</div>
</template>
Now when the editor opens the Media Library in blökkli, they can replace the referenced media entity on that paragraph by just drag and dropping the new image onto the existing one.
blökkli is loading the field configuration at runtime to determine which bundles are allowed.
Currently only media entity reference fields are supported, but there are plans to add support for any entity references.
Editing entity fields
blökkli also allows you to edit fields on a host entity such as a node. The syntax is identical. For example, to make the title of a node editable within blökkli, we would do this:
<template>
<BlokkliProvider v-slot="{ entity }" v-bind="blokkliProps" :entity="props">
<h1 v-blokkli-editable:title>
{{ entity?.title || title }}
</h1>
</BlokkliProvider>
</template>
There are a few things to note here:
When the title is edited, the new title is not stored on the node immediately - it's part of the edit state
Because we would normally display the "live" title of the node in the template,
we need a way to get the "edited" title during editing. blökkli does this
automatically as part of loading the edit state, but you have to tell blökkli
what to load. The fragment used to load this is called pbMutatedEntity
.
blökkli expects this fragment to exist - if it doesn't, then none of the GraphQL
queries and mutations will work.
It is recommended to not load the entire data for your node in this fragment, because it can significantly increase the request time for a query.
Let's say we only want to make the title editable on every node. Our fragment could look like this:
fragment pbMutatedEntity on Entity {
... on Node {
title
}
}
During editing the <BlokkliProvider>
component passes this "edited" object as
the entity
slot value:
<template>
<BlokkliProvider v-slot="{ entity }" v-bind="blokkliProps" :entity="props">
<h1 v-blokkli-editable:title>
<!-- Contains the edited title while editing. -->
{{ entity?.title }}
<!-- Always contains the title value of the "live" node. -->
{{ title }}
</h1>
</BlokkliProvider>
</template>
<script lang="ts" setup>
import type { NodePageFragment } from '#graphql-operations'
const props = defineProps<{
title?: string
blokkliProps: NodePageFragment['blokkliProps']
}>()
</script>
The type of the entity
object is inferred based on the type of :entity
. We
pass in the entire props
of the component as the :entity
prop.
For this reason, you will always need to write a fallback, e.g.:
{{ entity?.title || title }}
That way the title is always displayed, no matter if it has been edited or not.
A more complex example (as defined in the starterkit) would be this:
fragment pbMutatedEntity on Entity {
... on NodePage {
title
lead: fieldLead
hero: fieldHeroImage {
...mediaImage
}
}
... on NodePressRelease {
title
lead: fieldLead
image: fieldImage {
...mediaImage
}
}
}