Всё о quill blots, и то как сделать свой собственный

Часы
24.10.2024
Часы
17.12.2024
Часы
6 минут
Глазик
66
Сердечки
0
Соединённые точки
0
Соединённые точки
0
Соединённые точки
0

Список blot-ов (клякс) и то как их найти

Клякса (blot) - это строительный блок документа для parchment. Их различные виды, и способы применения, и реализации можно посмотреть на моём онлайн редакторе quill.
Как можно будет увидеть ниже, этих строительных блоков достаточно много. И каждый из них существует, чтобы выполнять определённую роль в документе. О роли каждого blot-а я расскажу отдельно. А пока, вот все виды клякс в quilljs:
  1. block
  2. block/embed
  3. break
  4. container
  5. cursor
  6. embed
  7. inline
  8. scroll
  9. text
Для того чтобы импортировать любой из этого списка blot, в новую переменную в JS, введи название blot-а после blots, например вот так:
let blot = Quill.imports["blots/block"]
Чтобы посмотреть полный список всех доступных blot-ов, введи в консоль браузера следующее:
Quill.imports

Про предназначение каждого из блоков

Inline blot. Примеры использования данного блока это bold, italik, или что-нибудь подчёркнутое. Это самый простой блок, он просто оборачивает выделенный в заранее указанный тег с заранее указанными классами. И он может быть обёрнут вокруг другого inline блока.
Block blot. К примеру можно привести блок цитат, или блок кода, или заголовки. Важно понять как работает данный блок. Он не просто оборачивает в определённый тег выделенный блок, да именно блок, не текст, он его заменяет. То есть блоки block не могут быть внутри друг друга, как это есть с inline блоками.
Container blot. Они созданы для того, чтобы преодолеть не возможность предыдущих блоков встраиваться в другие. Хотя и не без своих нюансов. В других статьях, вы увидите, что нам придётся заранее указывать элементы которые могут быть друг-в-друге ;) Примером реализации таких блоков являются списки и таблицы.
Block/embed и embed. Работают так же как и обычные блоки, но с той лишь разницей, что контент который они встраивают не может быть отредактирован. Его можно перезагрузить или удалить, но не изменить. Это видео, формулы, изображения.
Scroll blot. Представляет собой главный контейнер для всех остальных. Что он из себя представляет? По умолчанию это DIV элемент с классом ql-editor. Который может содержать следующие типы blot-ов:
  1. block
  2. embed
  3. container
Ещё одной важной особенностью данного blot-а является то, что конструктор первым параметром принимает специальный объект - регистр. Сейчас об регистрах quill рано говорить, но скажем так, каждый регистр это уникальный набор кнопок и форматеров для редактирования текста. То есть, на одной странице можно иметь несколько текстовых редакторов со своим уникальным функционалом.
Break blot. Это специальный нулевой-элемент. В Parchment библиотеке, каждый контейнер, у которого могут быть дети, должен иметь хотя бы одного. И под роль начального элемента создали этот blot.
Text blot. Представляет собой обычную обёртку вокруг текста
Cursor blot. Представляет собой вертикальную линию при редактировании. И знает, где и на каком блоке она находится.

Расширяем функционал blot-ов по умолчанию, их методы

В этой главе я собрал, все возможные методы для переопределения и набросал своих комментарий по поводу их работы. Сделал я это, ибо официальный сайт не обладает такой информацией, хотя его всё ещё можно найти через консоль браузера.

Block blot

Методы для собственной имплементации. Исходный код -> https://github.com/slab/parchment/blob/main/src/blot/block.ts
static create(value?: unknown);
static formats(domNode: HTMLElement, scroll: Root): any;
constructor(scroll: Root, domNode: Node);
format(name: string, value: any): void;
formats(): { [index: string]: any };
formatAt( index: number, length: number, name: string, value: any ): void;
insertAt(index: number, value: string, def?: any): void;
replaceWith(name: string | Blot, value?: any): Blot;
deleteAt(index: number, length: number): void;
delta(): void;
insertBefore(blot: Blot, ref?: Blot | null): void;
length(): number;
moveChildren(target: Parent, ref?: Blot | null): void;
optimize(context: { [key: string]: any }): void;
path(index: number) [Blot, number][];
removeChild(child: Blot): void;
split(index: number, force: boolean | undefined = false): Blot | null;
Переменные и их значения по умолчанию:
static blotName = 'block';
static scope = Scope.BLOCK_BLOT;
static tagName: string | string[] = 'P';
static allowedChildren: BlotConstructor[] = [InlineBlot, BlockBlot, LeafBlot ];
length: number;
name: string;

Inline blot

Методы для собственной имплементации. Исходный код -> https://github.com/slab/parchment/blob/main/src/blot/inline.ts
static create(value?: unknown): Node;
static formats(domNode: HTMLElement, scroll: Root): any;
static compare(self: string, other: string): number;
constructor(scroll: Root, domNode: Node);
format(name: string, value: any): void;
formats(): { [index: string]: any };
formatAt( index: number, length: number, name: string, value: any ): void;
optimize(context: { [key: string]: any }): void;
replaceWith(name: string | Blot, value?: any): Blot;
replaceWith(name: string | Blot, value?: any): Blot;
update( mutations: MutationRecord[], context: { [key: string]: any } ): void;
public wrap(name: string | Parent, value?: any): Parent;
Переменные и их значения по умолчанию:
static blotName = 'inline';
static order = [
'cursor',
'inline',
'link',
'underline',
'strike',
'italic',
'bold',
'script',
'code',
];
static scope = Scope.INLINE_BLOT;
static tagName: string | string[] = 'SPAN';
static allowedChildren: BlotConstructor[] = [InlineBlot, LeafBlot ];
length: number;
name: string;

Embed blot

Методы для собственной имплементации. Исходный код -> https://github.com/slab/parchment/blob/main/src/blot/embed.ts
static create(value?: unknown): Node;
static formats(_domNode: HTMLElement, _scroll: Root): any;
constructor(scroll: Root, domNode: Node);
format(name: string, value: any): void;
formatAt( index: number, length: number, name: string, value: any ): void;
formats(): { [index: string]: any };
index(node: Node, offset: number): number;
restore(node: Text): EmbedContextRange | null;
update(mutations: MutationRecord[], context: Record<string, unknown>): void;
Переменные и их значения по умолчанию:
static scope = Scope.INLINE_BLOT;
length: number;
name: string;
contentNode: HTMLSpanElement;
leftGuard: Text;
rightGuard: Text;

Cursor blot

Методы для собственной имплементации.
static create(value?: unknown): Node;
static formats(_domNode: HTMLElement, _scroll: Root): any;
constructor(scroll: ScrollBlot, domNode: HTMLElement, selection: Selection): void;
detach(): void;
format(name: string, value: unknown): void;
formatAt( index: number, length: number, name: string, value: any ): void;
formats(): { [index: string]: any };
index(node: Node, offset: number): number;
length(): number;
position(): [Text, number];
remove(): void;
restore(): EmbedContextRange | null;
update(mutations: MutationRecord[], context: Record<string, unknown>): void;
optimize(context?: unknown): void;
value() string; // Will return ''
Переменные и их значения по умолчанию:
static blotName = 'cursor';
static className = 'ql-cursor';
static tagName = 'span';
static CONTENTS = '\uFEFF'; // Zero width no break space
length: number;
name: string;

Text blot

Методы для собственной имплементации. Исходный код -> https://github.com/slab/parchment/blob/main/src/blot/text.ts
static create(value: string): Node;
static value(domNode: Text): string;
constructor(scroll: Root, node: Node);
deleteAt(index: number, length: number): void;
index(node: Node, offset: number): number;
insertAt(index: number, value: string, def?: any): void;
length(): number
optimize(context: { [key: string]: any }): void;
position(index: number, _inclusive = false): [Node, number];
split(index: number, force = false): Blot | null;
update( mutations: MutationRecord[], _context: { [key: string]: any } ): void;
value(): string;
Переменные и их значения по умолчанию:
static readonly blotName = 'text';
domNode: Text;
static scope = Scope.INLINE_BLOT;

Container blot

Методы для собственной имплементации. Исходный код -> https://github.com/slab/parchment/blob/main/src/blot/abstract/container.ts
static create(value: string): Node;
constructor(scroll: Root, domNode: Node);
checkMerge(): boolean;
deleteAt(index: number, length: number): void ;
formatAt( index: number, length: number, name: string, value: any ): void;
optimize(context: { [key: string]: any }): void;
insertAt(index: number, value: string, def?: any): void;
Переменные и их значения по умолчанию:
static blotName = 'container';
static scope = Scope.BLOCK_BLOT;
static tagName: string | string[];
prev: BlockBlot | ContainerBlot | null;
next: BlockBlot | ContainerBlot | null;

Scroll blot

Методы для собственной имплементации. Исходный код -> https://github.com/slab/parchment/blob/main/src/blot/scroll.ts
static create(value: string): Node;
constructor( public registry: Registry, node: HTMLDivElement, { emitter }: { emitter: Emitter } );
batchStart(): void;
batchEnd(): void;
emitMount(blot: Blot): void;
emitUnmount(blot: Blot): void;
emitEmbedUpdate(blot: Blot, change: unknown): void;
deleteAt(index: number, length: number): void;
enable(enabled = true): void;
formatAt(index: number, length: number, format: string, value: unknown): void;
insertAt(index: number, value: string, def?: unknown): void;
insertBefore(blot: Blot, ref?: Blot | null): void;
insertContents(index: number, delta: Delta): void;
isEnabled(): boolean;
leaf(index: number): [LeafBlot | null, number];
line(index: number): [Block | BlockEmbed | null, number];
lines(index = 0, length = Number.MAX_VALUE): (Block | BlockEmbed)[]
optimize(context?: { [key: string]: any }): void;
optimize( mutations?: MutationRecord[], context?: { [key: string]: any }): void;
optimize(mutations = [], context = {}): void;
path(index: number): number;
remove(): void; // Do nothing
update(source?: EmitterSource): void;
update(mutations?: MutationRecord[]): void;
update(mutations?: MutationRecord[] | EmitterSource): void;
updateEmbedAt(index: number, key: string, change: unknown): void;
Переменные и их значения по умолчанию:
static blotName = 'scroll';
static className = 'ql-editor';
static tagName = 'DIV';
static defaultChild = Block;
static allowedChildren = [Block, BlockEmbed, Container];
emitter: Emitter;
batch: false | MutationRecord[];

Как создать новый blot из старого или переопределить существующий

Для примера переопределения существующего blot-а я буду использовать block blot. Начнём с импорта.
let Block = Quill.import('blots/block');
Дальше, нужно определить новый функционал blot-а. В моём случае, я хочу чтобы вместо тега <p> использовался <div> и добавлялся класс по имени "txt". Вот так я это сделаю:
Block.tagName = 'div'
class TextBlock extends Block{

static create(value){
let node = super.create(value);
node.setAttribute('class','txt');
return node;
}
}
// Регистрируем (обновляем blot block)
Quill.register(TextBlock, true)
Это по части модификации blot-ов. Но как сделать новый? У любого quill-блока есть 3 переменные, которые используются для отличия одного блока от другого. Первый это tagName, второй blotName и третий className.
Для создания нового blot-а, нужно задать другое значение для blotName. Я продемонстрирую это на примере ссылок. Мы создадим 2 варианта ссылок:
  1. Внутренние ссылки
  2. Внешние ссылки
// Импортируем необходимый blot
let Inline = Quill.import('blots/inline')

// Задаём значения для нового blot-а внутренней ссылки
class InternalLink extends Inline{
constructor(scroll, domNode){
super(scroll, domNode);
domNode.addEventListener('click', (ev) => {
domNode.setAttribute('ref', 'me')
})
}
}
InternalLink.tagName = 'a'
InternalLink.className = 'ref-int'
// Меняем имя blot-а
InternalLink.blotName = 'internal-link'
// Регистрируем новый форматер
Quill.register({'formats/internal-link': InternalLink})

// Задаём значения для нового blot-а внешней ссылки
class ExternalLink extends Inline{
constructor(scroll, domNode){
super(scroll, domNode);
domNode.setAttribute('target', '_blank')
domNode.setAttribute('ref', 'noreferrer nofollow external')
}
}
ExternalLink.tagName = 'a'
ExternalLink.className = 'ref-ext'
// Меняем имя blot-а
ExternalLink.blotName = 'external-link'
// Регистрируем новый форматер
Quill.register({'formats/external-link': ExternalLink})
Ты наверное думаешь, что такое этот "форматер". Но по сути, я просто сделал кнопку для создания и удаления этого типа blot-а в редакторе.

Комментарии

(0)

captcha
Отправить
Сейчас тут пусто. Буть первым (o゚v゚)ノ

Другое

Похожие статьи


Quill formats, их расширение и виды

Часы
24.10.2024
В этой статье я разберу типы форматеров quilljs, и объясню некоторые нюансы работы с таблицами, шрифтами, изображениями и видео. Так же покажу как переопределить форматер на примере ссылки

Собственный quill tooltip, как сделать и как работает

Часы
25.10.2024
В этой статье ты найдёшь пример того как реализовать свой quill тултип и поймёшь как это вообще работает. Для примера взяты тултипы для ссылок