GithubHelp home page GithubHelp logo

blokkli / editor Goto Github PK

View Code? Open in Web Editor NEW
44.0 5.0 2.0 6.38 MB

Interactive page editor for Nuxt 3

Home Page: https://blokk.li

License: MIT License

TypeScript 38.08% CSS 11.25% JavaScript 1.03% Vue 49.64%
editor nuxt page-builder wysiwyg

editor's Introduction

blökkli page builder for Nuxt

Live Demo

blökkli is an interactive page editor that integrates with any backend and offers various features to edit content quick and easy.

Screenshot of the blökkli editor

Features

  • Smooth drag and drop editing
  • True WYSIWYG
  • Dynamic options for block apperance
  • Inline text editing (plain or rich text)
  • Multilanguage (both UI and content)
  • Nested blocks (for sections, grids, accordions, etc.)
  • Restrictions (allowed block types, cardinality, max instances)
  • Clipboard integration (paste text, images, links or copy blocks)
  • Comments
  • and many more

Comparison to other editors

The main difference to other WYSIWYG editors is that blökkli does not manage the data - it's just the editor. It provides a single interface (called adapter) to integrate any backend or data structure. Actually performing the mutations (like add, delete, move, setting options) is left to the adapter. In most cases an adapter method performs API calls, but it could as well all be done in the browser. In fact, the demo page / playground implements a reference adapter that stores mutations in local storage and keeps data in JSON files.

Integration with Nuxt

One of the main goals of blökkli is to offer a seamless integration with your new or existing Nuxt app. Creating new block components is straightforward. Options to change the appearance of a block are directly defined in the component, so is configuration that defines the behaviour of the block when rendered in the editor.

During normal rendering of blocks (not in the editor), the provided blökkli components and composables have a minimal overhead, to not have a negative impact on performance.

Backends

Currently blökkli ships with an adapter to integrate it with the paragraphs module of Drupal. The Paragraphs blökkli module implements all features available in the editor. It provides a new edit state entity that stores the applied mutations and implements a GraphQL schema extension to integrate the adapter.

Roadmap

blökkli is currently still in beta and therefore subject to change. If you're considering integrating it in production sites you should probably wait until the first stable release.

Acknowledgments

blökkli was developed by dulnan at Liip. The development was supported by the canton of Basel-Stadt in Switzerland as part of the relaunch project for WebBS.

editor's People

Contributors

dasjo avatar dulnan avatar jonock avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

editor's Issues

Improve editing of reusable blocks

Currently reusable blocks can‘t be edited (which is by design). There should be a way to enter a mode where a single reusable block can be edited, standalone. It should be clear that editing it affects all usages, which is why standalone editing is preferred.

Basic setup returns error: "Failed to locate artboard element."

Hello @dulnan @dasjo and @jonock !

I am trying the first instance of blokk.li with my fresh nuxt.js local setup and I am getting exceptions whenever I tap on the button to "Edit Blocks". Basically I've not invented any new functionalities, just trying to follow the doc so far.

Please find the versions and environment information below which may help you to diagnose the problem. Any help is much appreciated, the package looks really promising.

I am using: 18.14.2 node version on my Ubuntu 22.04 Chrome and Brave browsers.

Package.json:

{
  "name": "nuxt-app",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
  "dependencies": {
    "blokkli-beta": "^0.0.1-beta.0",
    "nuxt": "^3.11.1",
    "vue": "^3.4.21",
    "vue-router": "^4.3.0"
  }
}

nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['blokkli-beta'],

  blokkli: {
    // Only required configuration.
    // Should match the entity type of your blocks.
    itemEntityType: 'block',
  },
})

app/blokkli.editAdapter.ts:

import { defineBlokkliEditAdapter } from '#blokkli/adapter'

type YourStateType = {
  page: {
    title: 'My page'
  }
  editState: {
    currentMutationIndex: number
    mutations: any[]
    mutatedFields: any[]
    violations: any[]
    // etc.
  }
}

const bundles: BlockBundleDefinition[] = [
    {
      id: 'card',
      label: 'Card',
      description:
        'A block that renders a card with a title, text and a link.',
      allowReusable: true,
      isTranslatable: true,
    },
    {
      id: 'horizontal_rule',
      label: 'Horizontal Rule',
      description:
        'A block that renders a simple horizontal rule to separate content.',
      allowReusable: false,
      isTranslatable: false,
    },
    // etc.
  ]

export default defineBlokkliEditAdapter<YourStateType>((ctx) => {
    const mapState = (yourState: YourStateType): MappedState => {
        return {
          currentIndex: yourState.editState.currentMutationIndex,
          mutations: yourState.editState.mutations.map(mapMutation),
          currentUserIsOwner: yourState.page.owner.id === user.value.id,
          ownerName: yourState.page.owner.name,
          mutatedState: {
            fields: yourState.editState.mutatedFields.map(mapMutatedFields),
          },
          // etc.
        }
    }

  return {
    loadState: async (): YourStateType => {
      const response = await $fetch(
        `/backend-api/edit/${ctx.value.entityUuid}/get-state`,
      )
      return response.data
    },
    mapState,
    getAllBundles: () => {
        return Promise.resolve(bundles)
    },
    getFieldConfig: () => {
        const fieldConfig: FieldConfig[] = [
          {
            // Same name as on <BlokkliField>.
            name: 'field_blocks',
  
            // Same type and bundle of the <BlokkliProvider> the field
            // belongs to.
            entityType: 'content',
            entityBundle: 'blog_post',
  
            // The label displayed in the editor.
            label: 'Blocks',
  
            // The maximum number of blocks in this field. -1 means unlimited.
            cardinality: -1,
  
            // Whether the current user is allowed to edit the field.
            canEdit: true,
  
            // The block bundles that are allowed in this field.
            allowedBundles: ['title', 'rich_text', 'link', 'horizontal_rule'],
          },
        ]
        return Promise.resolve(fieldConfig)
    },
    addNewBlock: (e) => {
        return $fetch(`/api/edit/${ctx.value.entityUuid}/add-new-block`, {
          method: 'post',
          body: {
            // The block bundle to add.
            bundle: e.type,
  
            // The parent entity type where the block is being added.
            // Could be the entity type of the <BlokkliProvider> or in case
            // of nested blocks, the entity type of the block.
            entityType: e.host.type,
            entityUuid: e.host.uuid,
  
            // The field name where the block is added.
            fieldName: e.host.fieldName,
  
            // The UUID of the block that should be before the new one.
            preceedingUuid: e.afterUuid,
          },
        })
    },
    moveBlock: (e) => {
        return $fetch(`/backend-api/edit/${ctx.value.entityUuid}/move-block`, {
          method: 'post',
          body: {
            // The block being moved.
            uuid: e.item.uuid,
  
            // The parent entity type where the block is being added.
            // Could be the entity type of the <BlokkliProvider> or in case of nested blocks, the entity type of the block.
            entityType: e.host.type,
            entityUuid: e.host.uuid,
  
            // The field name where the block is added.
            fieldName: e.host.fieldName,
  
            // The UUID of the block that should be before the new one.
            // If undefined, the block should be moved to index 0 of the field list.
            preceedingUuid: e.afterUuid,
          },
        })
    },
    moveMultipleBlocks: (e) => {
        return $fetch(
          `/backend-api/edit/${ctx.value.entityUuid}/move-multiple-blocks`,
          {
            method: 'post',
            body: {
              // The UUIDs of the blocks being moved.
              uuids: e.uuids,
  
              // The parent entity type where the block is being added.
              // Could be the entity type of the <BlokkliProvider> or in case of nested blocks, the entity type of the block.
              entityType: e.host.type,
              entityUuid: e.host.uuid,
  
              // The field name where the block is added.
              fieldName: e.host.fieldName,
  
              // The UUID of the block that should be before the new one.
              // If undefined, the block should be moved to index 0 of the field list.
              preceedingUuid: e.afterUuid,
            },
          },
        )
    },
  }
})

app.vue:

<template>
  <BlokkliProvider
    entity-type="content"
    entity-bundle="blog_post"
    entity-uuid="9485812c-0ecd-4699-85b2-3a031d47a0a1"
    can-edit
  >
    <BlokkliField :list="blocks" name="blocks" />
  </BlokkliProvider>
</template>

<script lang="ts" setup>
const blocks = [
  {
    uuid: '96f7fbda-04d9-4662-9a6c-6aa3bcf964f2',
    bundle: 'title',
    props: {
      title: 'Hello world',
    },
  },
  {
    uuid: '9485812c-0ecd-4699-85b2-3a031d47a0a1',
    bundle: 'rich_text',
    props: {
      text: '<p>Lorem ipsum dolor sit amet</p>',
    },
  },
  {
    uuid: '5407ef47-dfbc-456b-9900-8e2577185380',
    bundle: 'horizontal_rule',
  },
  {
    uuid: 'f91e046b-3bcc-48dd-bbd5-8a21115436b7',
    bundle: 'link',
    options: {
      linkType: 'button',
    },
    props: {
      label: 'Learn more',
      href: '/learn-more',
    },
  },
]
</script>

components/Blocks/Title.vue:

<template>
  <h2>{{ title }}</h2>
</template>

<script lang="ts" setup>
defineBlokkli({
  bundle: 'title',
})

defineProps<{
  title: string
}>()
</script>

Here's the output snapshot:

image

The error says:

uiProvider.mjs?v=e08a98ab:31 Uncaught (in promise) Error: Failed to locate artboard element.
    at artboardElement (uiProvider.mjs?v=e08a98ab:31:13)

Please let me know what I am missing, really looking forward to take this further. Thanks!

Improve vertical scroll speed

Currently, when you scroll vertically within the editor, it feels a lot slower than on regular web pages.

Goal: provide a fast scroll experience (ideally on-par with how the web browser scrolls by default)

schema.json export is randomly sorted.

When generating the schema.json using schemaOptionsPath for the options, the ordering of the option is random and constantly generates diffs in git.

Please sort the options by key name to avoid the issue when exporting the schema.json.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.