TypeCellOS / BlockNote

A React Rich Text Editor that's block-based (Notion style) and extensible. Built on top of Prosemirror and Tiptap.
https://www.blocknotejs.org/
Mozilla Public License 2.0
5.89k stars 381 forks source link

Error: initialContent must be a non-empty array of blocks, received: #890

Closed softmarshmallow closed 1 day ago

softmarshmallow commented 2 days ago

Passing a empty array [] to initialContent will throw an error. This makes sense but also, the editor should be able to use without case handling by users, where for my case I have a postgresql table with body default value to [] - this can be safely ignored by BlockNote. (Plus I don't see a reason why this should be trhown internally as well.)

"use client";

import { ThemedRichTextEditorContent } from "@/components/richtext";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { BlockNoteSchema, defaultBlockSpecs } from "@blocknote/core";
import { useCreateBlockNote } from "@blocknote/react";

const { table: _noop1, ...remainingSpecs } = defaultBlockSpecs;
const schema = BlockNoteSchema.create({
  blockSpecs: remainingSpecs,
});

export function RichTextEditCell({ defaultValue }: { defaultValue?: any }) {
  const editor = useCreateBlockNote({
    schema: schema,
    // this will throw when `[]` is passed
    initialContent: defaultValue,
  });

  return (
    <Dialog defaultOpen>
      <DialogContent className="min-w-full h-full max-w-lg">
        <div className="prose dark:prose-invert mx-auto w-full">
          <ThemedRichTextEditorContent
            onKeyDown={(e) => {
              // this is required for preventing exit on enter pressed
              e.stopPropagation();
            }}
            editor={editor}
          />
        </div>
      </DialogContent>
    </Dialog>
  );
}

I can do below, but should I?

function safevalue(value: any) {
  if (Array.isArray(value) && value.length > 0) {
    return value;
  }
  return undefined;
}

export function RichTextEditCell({ defaultValue }: { defaultValue?: any }) {
  const editor = useCreateBlockNote({
    schema: schema,
    initialContent: safevalue(defaultValue),
  });

  return (
    <Dialog defaultOpen>
      <DialogContent className="min-w-full h-full max-w-lg">
        <div className="prose dark:prose-invert mx-auto w-full">
          <ThemedRichTextEditorContent
            onKeyDown={(e) => {
              // this is required for preventing exit on enter pressed
              e.stopPropagation();
            }}
            editor={editor}
          />
        </div>
      </DialogContent>
    </Dialog>
  );
}
YousefED commented 1 day ago

This is by design: passing undefined makes BlockNote initialize the editor with an empty paragraph block. I think this is a common pattern (use a default value, when passed undefined). I think using a default value (empty paragraph block) when passing [] would not be very clear for consumers; hence the error.

(I think both approaches can be valid ofc)

softmarshmallow commented 12 hours ago

Hi. If I may, I have some follow up questions, Not a big expert on neither prosemirror nor tiptap, but as I understand, the docuement model seems like a tiptap's json wihout a root doc, is this correct? - It may confuse some people moving from tiptap since the document model does not match (I personally expected the way working with blocknotes' document model would be identical to tiptap.

Not a big problem though!

matthewlipski commented 2 hours ago

Hi. If I may, I have some follow up questions, Not a big expert on neither prosemirror nor tiptap, but as I understand, the docuement model seems like a tiptap's json wihout a root doc, is this correct? - It may confuse some people moving from tiptap since the document model does not match (I personally expected the way working with blocknotes' document model would be identical to tiptap.

Not a big problem though!

Actually, BlockNote's editor.document is a bit different from TipTap's JSON representation, though admittedly it's mostly a difference in naming. The reason it's different is so that we can have strong typing - TipTap doesn't care what you pass to commands like setContent whereas BlockNote is strict on what you can pass to e.g. replaceBlocks.

This is because you can have huge flexibility with the schema in TipTap, but BlockNote is more rigid, and the general structure of blocks can't be changed. But you're right, the way that the document is represented in TipTap using JSON vs how it's represented in BlockNote with Block objects is pretty close.