Skip to content

Max level depth implementation #80

@ccppoo

Description

@ccppoo
import { BlockToolConstructorOptions } from '@editorjs/editorjs/types/tools';
import type { NestedListParams } from '@editorjs/nested-list';
import NestedList from '@editorjs/nested-list';


// copied from original code == start ==
type ListDataStyle = 'ordered' | 'unordered';

interface ListItem {
  /**
   * list item text content
   */
  content: string;
  /**
   * sublist items
   */
  items: ListItem[];
}
interface ListData {
  /**
   * list type 'ordered' or 'unordered'
   */
  style: ListDataStyle;
  /**
   * list of first-level elements
   */
  items: ListItem[];
}
interface NestedListConfig {
  /**
   * default list style: ordered or unordered
   * default is unordered
   */
  defaultStyle?: ListDataStyle;
}

// copied from original code == end ==

// this is also implemented in package but couldn't import from dist source, so I just copied it
function isHtmlElement(node: Node): boolean {
  return node instanceof HTMLElement;
}

// interface for config, 
interface NestedListCustomParams {
  maxDepth?: number;
}

// extending constructor params
export type _NestedListParams = BlockToolConstructorOptions<
  ListData,
  NestedListConfig & NestedListCustomParams
>;

export default class LimitedNestedList extends NestedList {
  constructor({ data, config: _config, api, readOnly }: _NestedListParams) {
    const { maxDepth, ...config } = _config;
    // need to cast to original NestedListParams interface 
    super({ data, config, api, readOnly } as NestedListParams);

    if (!maxDepth || maxDepth < 1) {
      // min : 1 (could nest like : 1.1, 1.2, 2.1, 2.2, ... )
      // default : 2 (could nest like : 1.1.1, 1.2.3, ... )
      this.maxDepth = 2;
      return;
    }
    this.maxDepth = maxDepth;
  }

  private maxDepth: number;

  addTab(event: KeyboardEvent): void {
    // same as original code == start ==
    event.stopPropagation();
    event.preventDefault();

    const currentItem = this.currentItem;
    if (!currentItem) {
      return;
    }
    const prevItem = currentItem.previousSibling;
    if (!prevItem) {
      return;
    }
    if (!isHtmlElement(prevItem)) {
      return;
    }
    const isFirstChild = !prevItem;

    if (isFirstChild) {
      return;
    }
    // same as original code == end ==

    // find until element class name is  : `ce-block__content`
    const maxHTMLDepth = 2 + 3 * (this.maxDepth - 1); // 0, 2, 5, 8, 11, ...
    const rootName = 'ce-block__content';
    let HTMLDepth = 0;
    let element: Element | null = currentItem;
    while (element?.className !== rootName) {
      element = element?.parentElement || null;
      HTMLDepth += 1;
      if (HTMLDepth > maxHTMLDepth) return;
    }

    return super.addTab(event);
  }
}
const nestedListTool = {
  class: LimitedNestedList,
  inlineToolbar: true,
  config: {
    defaultStyle: 'ordered',
  },
};

....

const editorConfig = {
    tools: {
        list : nestedListTool 
        ....
    }
}

I tried to make as finding root element, or Block by using block data

but it was quite hard to override methods, and accessing private variables.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions