blökkli Editor

repository·main·Indexed 41 days ago

https://github.com/blokkli/editor

An interactive, true WYSIWYG page builder for Nuxt applications. blökkli acts as a pure editor interface that uses adapters to manage data mutations across various backends, including a specialized adapter for the Drupal Paragraphs module. Version 1.3.4.

Tokens
53.2K
Snippets
138
Records
173
Agent score
51%

What's inside blökkli

  1. What is blökkli and how does it work?

    main

    blökkli is an interactive page builder designed for Nuxt. Unlike traditional WYSIWYG editors, blökkli does not manage data itself. Instead, it acts as a pure editor interface that communicates with your data via an adapter.

    An adapter provides a single interface to integrate any backend or data structure. The adapter is responsible for performing mutations such as adding, deleting, moving, or setting options on blocks. These mutations typically involve API calls to a backend, though they can also be performed locally (e.g., using local storage or JSON files).

  2. What is a blökkli adapter and why is it needed?

    main

    blökkli provides the editor UI but does not handle data persistence. An adapter is the layer responsible for implementing the logic to save and retrieve changes. Without an adapter, the editor cannot persist data to a backend.

    Currently, blökkli includes a built-in adapter for integrating with a Drupal paragraphs-based setup using GraphQL.

  3. Define the block data structure

    main

    Blökkli renders block components based on an array of block items. Each item in the array must follow this structure:

    • uuid: (String) The unique ID of the block.
    • bundle: (String) The specific block bundle (sub-type) to be rendered.
    • props: (Object, optional) Values passed directly as props to the block component.
    • options: (Object, optional) Additional configuration for the block.
    {
      "uuid": "96f7fbda-04d9-4662-9a6c-6aa3bcf964f2",
      "bundle": "title",
      "props": {
        "title": "Hello world"
      }
    }
  4. Provide option values in the block data structure

    main

    When defining or fetching block data (e.g., from a database or a local array), you provide the runtime values for a block's configuration using an options property. This property is a simple key-value object where the key matches the option name defined in the component, and the value is the selected setting.

    This allows the BlokkliField to pass the correct configuration down to the rendered component.

    <script lang="ts" setup>
    const blocks = [
      {
        uuid: '5407ef47-dfbc-456b-9900-8e2577185380',
        bundle: 'horizontal_rule',
        options: {
          size: 'large',
        },
      },
    ]
    </script>
  5. How BlokkliProvider context and editing works

    main

    The <BlokkliProvider> component manages the editing context for a single host entity.

    Key Constraints:

    • Single Entity Scope: Each provider instance manages exactly one entity. If multiple providers exist on a single page (e.g., one for main content and one for a footer), each will have its own independent "Edit blocks" button.
    • No Cross-Entity Drag-and-Drop: Because the editor is bound to a specific provider context, you cannot move blocks between two different parent entities at the same time.

    Activating the Editor via URL:

    The editor only mounts when two conditions are met:

    1. The :can-edit prop is true.
    2. The blokkliEditing query parameter in the URL matches the :entity-uuid prop value.

    Example URL structure: /en/topics/my-test-page?blokkliEditing=908fac9f-e47e-4478-bfd8-ab8ac947835b

  6. Understand the Edit State lifecycle

    main

    When the blökkli editor is mounted, it ignores the initial block items array passed to <BlokkliField :list="block">. Instead, it relies on an 'edit state' loaded via the adapter's loadState() method.

    Key behaviors:

    • Data Source: The editor uses mutatedState.fields to render blocks instead of the original props.
    • Option Overrides: Block options are overridden by mutatedState.mutatedOptions.
    • State Updates: The state object must be updated after every mutation (e.g., moving blocks). The adapter's mutation methods (like moveBlocks()) are expected to return the updated state object.
  7. What are blökkli features?

    main

    In blökkli, a feature (also referred to as a "module" or "plugin") is a single component that provides a specific set of editor capabilities, such as comments, block duplication, or clipboard integration.

    Features are highly flexible and can be enabled or disabled at either build time or runtime (for example, enabling a translation feature only when the language context indicates the user is editing translated content). Because most core functionality is implemented as features, you can completely override a built-in feature with your own custom implementation.

  8. Manage user ownership and permissions

    main

    The edit state controls whether a user can edit content via ownership properties:

    • currentUserIsOwner (boolean): If false, the user sees a banner with a 'take ownership' button. If true, they can edit normally. To disable the ownership feature entirely, always return true.
    • ownerName (string): The name of the current owner, displayed in the banner if the current user is not the owner.
    • takeOwnership(): When the user clicks the banner button, the adapter's takeOwnership() method must be called to update the owner.
  9. Manage entity relationships and parentage

    main

    To ensure content isolation (so that editing a block on one page doesn't affect another), every block must belong to exactly one parent entity. This is achieved by mapping the block to a parent_entity_type and a parent_entity_uuid.

    Database Schema Representation:

    entity_typeentity_bundleuuidparent_entity_typeparent_entity_uuid
    blocktitle1content123
    blockrich_text2content123
    blocktitle3content123
    blocktitle4content5192
    | entity_type | entity_bundle | uuid | parent_entity_type | parent_entity_uuid |
    | ----------- | ------------- | ---- | ------------------ | ------------------ |
    | block       | title         | 1    | content            | 123                |
    | block       | rich_text     | 2    | content            | 123                |
    | block       | title         | 3    | content            | 123                |
    | block       | title         | 4    | content            | 5192               |
  10. Data structure for nested blocks

    main

    When defining a data structure that includes nested blocks, the nested items are represented as an array of block objects within the props of the parent block. Each nested block object must include a uuid, a bundle name, and its own props object.

    const blocks = [
      {
        uuid: 'parent-uuid',
        bundle: 'grid',
        props: {
          cards: [
            {
              uuid: 'child-uuid',
              bundle: 'card',
              props: {
                title: 'My Card Title',
              },
            },
          ],
        },
      },
    ]
  11. Assign entity UUIDs

    main

    Every entity must have an entity UUID. While it does not strictly have to be a standard UUID format (it can be strings like page_213 or integers like 1), it cannot be empty.

    Requirement: While UUIDs should ideally be unique across all entities, the strict requirement is that blocks must have a unique UUID to prevent collisions.

  12. What are fragments in blökkli?

    main

    Fragments are non-editable content components that can be reused across multiple pages. They are useful for elements like CTA sections, pricing tables, or contact forms that are defined by your application rather than being driven by backend data.

    Technically, a fragment is rendered using a special block bundle named blokkli_fragment. To use fragments, your backend must provide a block using this bundle, which stores the name of the fragment to be rendered.