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 nuxt.config.ts
file.
See the inline comments for more information on the configuration options.
export default defineNuxtConfig({
blokkli: {
// The path to the components blökkli should provide.
pattern: ['components/Paragraph/**/*.{js,ts,vue}'],
// Global options that are available for all components.
// The 3 spacing options could be used on multiple components.
globalOptions: {
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',
},
},
},
},
// Translation for the edit indicator.
translations: {
en: {
editIndicatorLabel: 'Edit paragraphs',
},
de: {
editIndicatorLabel: 'Abschnitte bearbeiten',
},
},
chunkNames: ['global', 'rare'],
// The entity type that should be used for the items
itemEntityType: 'paragraph',
storageDefaults: {
// Put some paragraph bundles into the favorites.
blockFavorites: ['text'],
},
// Provide a schema for the optiopns that can be consumed by Drupal.
// The paragraphs_blokkli module will use this schema file to
// allow the editor to configure the components with paragraphs behaviours.
schemaOptionsPath: IS_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. To use the
Drupal adapter, the file should contain this code:
import { defineBlokkliEditAdapter } from '#blokkli/adapter'
import drupalGraphlMiddlewareAdapter from '#blokkli/adapter/drupal/graphqlMiddleware'
export default defineBlokkliEditAdapter(drupalGraphlMiddlewareAdapter)
You can extend/override any adapter method to add custom functionality:
import { defineBlokkliEditAdapter } from '#blokkli/adapter'
import drupalGraphlMiddlewareAdapter from '#blokkli/adapter/drupal/graphqlMiddleware'
export default defineBlokkliEditAdapter(async (ctx) => {
const adapter = await drupalGraphlMiddlewareAdapter(ctx)
return {
...adapter,
clipboardMapBundle: function (e) {
// Custom logic to check if the pasted text can be used to create a paragraph.
if (e.type === 'plaintext' && e.text.length > 1000) {
// Text is too long, return.
return null
}
// Call the default implementation.
return adapter.clipboardMapBundle(e)
},
}
})
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
}
}
}