Можно ли создать собственный формат / блот со сложной подструктурой?

Я исследую использование Quill для проекта, и мне нужно знать, можно ли создать собственный формат / блот с большей сложностью, чем один элемент и один параметр.

Пример одного из макетов, который я хочу:

<span class="format-container">
    <span class="format-info" data-attr="param 1 (non-displayed)">
        param 2 (displayed to user -- click to invoke application UI to edit)
    </span>
    <span class="format-content">
        User's text/child elements go here
    </span>
</span>

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

Пользовательские форматы в Quill в настоящее время не очень хорошо документированы. Я покопался в источниках и смог понять, что это, скорее всего, невозможно в 0.20.1. Тем не менее, я чувствую, что это может быть выполнено в бета-версии 1.0.0 с пергаментом, я просто не уверен в специфике того, что мне действительно нужно написать.

Возможно ли это в 1.0.0? Если да, то как это можно было сделать?

РЕДАКТИРОВАТЬ: Это то, что я собираюсь сделать: Пример


person Stephen Cohen    schedule 13.06.2016    source источник
comment
Интересно, что у меня почти то же самое требование. Я пробовал Embed, но он всегда заставлял мой контент выделять новую строку, что приводило в ярость. Вы когда-нибудь догадывались об этом?   -  person Daniel Lane    schedule 01.08.2016
comment
Я потратил кучу времени, копаясь в исходном коде библиотеки, и на самом деле не думаю, что это возможно. Несмотря на то, что пергамент является абстракцией DOM, он по-прежнему полагается на отношения родитель / потомок DOM в некоторых местах, что означает, что наличие дочерних элементов в подобном субконтейнере может привести к поломке. В конце концов, я просто переработал свою собственную систему форматирования, так что в этом не было необходимости.   -  person Stephen Cohen    schedule 01.08.2016


Ответы (2)


Итак, я в итоге разобрался, как это сделать с минимальными усилиями. Он включает определение нового типа блота для Quill 1.3 или выше, тот же код должен работать в более старых версиях, но не тестировался.

См. Рабочий пример во фрагменте кода. Суть в том, чтобы расширить существующий блот Embed blots / embed и определить собственный обработчик панели инструментов для внедрения произвольных экземпляров узла DOM.

// utility function used to inherit non prototypical methods/properties
function extend(target, base) {
  for (var prop in base) {
    target[prop] = base[prop];
  }
}

// definition of a custom Blot.
(function(Embed) {
  'use strict';

  function Span() {
    Object.getPrototypeOf(Embed).apply(this, arguments);
  }

  Span.prototype = Object.create(Embed && Embed.prototype);
  Span.prototype.constructor = Span;
  extend(Span, Embed);

  Span.create = function create(value) {
    return value; // expects a domNode as value
  };

  Span.value = function value(domNode) {
    return domNode;
  };

  Span.blotName = 'span';
  Span.tagName = 'SPAN';
  Span.className = 'complex';

  Quill.register(Span, true);
})(Quill.import('blots/embed')); // import the embed blot. This is important as this is being extended

// custom handler for the toolbar button
var handler = function() {
  var complexSpan = document.getElementById('complextype').firstElementChild.cloneNode(true);
  var selection = quill.getSelection();

  quill.insertEmbed(selection.index, 'span', complexSpan);
}

// instantiate quill. Note that modules.toolbar.handlers has a 'span' handler. Quill parses this from // the class on the button in the toolbar markup: 'ql-span' this is 'ql-' + blotName
var quill = new Quill('#editor', {
  modules: {
    toolbar: {
      container: '.toolbar',
      handlers: {
        'span': handler
      }
    }
  },
  theme: 'snow'
});
:root {
  --complex-bgcolor: #767676;
  --font-color: #FFFFFF;
}

html {
  font-size: 10px;
}

button.ql-span {
  width: 15rem !important;
}

.complex {
  border-radius: 1rem;
  border: 0.2rem solid black;
  margin: 0.3rem;
}

.inner {
  border-radius: 1rem;
  background: var(--complex-bgcolor);
  color: var(--font-color);
  padding-left: 0.6rem;
  padding-right: 0.6rem;
}

.formatting {
  font-weight: bold;
  font-style: italic;
  text-decoration: underline;
}

.nested {
  margin-left: 0.3rem;
  margin-right: 0.3rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.1/quill.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.1/quill.snow.css" />
<div id="wrapper">
  <div id="toolbar" class="toolbar">
    <span class="ql-formats">
      <button class="ql-bold"></button>
      <button class="ql-italic"></button>
      <button class="ql-underline"></button>
    </span>
    <span class="ql-formats">
      <button class="ql-span">Complex Span Type</button>
    </span>
  </div>

  <div id="editor">Lorem Ipsum 
    <span contenteditable="false">
      <span class="complex" spellcheck="false">
        <span class="inner">Format applied</span>
        <span class="nested">More text</span>
        <span class="formatting">with formatting</span>
        <span class="nested">dolor</span>
      </span>
    </span> sit amet
  </div>
</div>

<div id="complextype" style="display:none;">
<span contenteditable="false"><span class="complex" spellcheck="false"><span class="inner">Format applied</span><span class="nested">More text</span><span class="formatting">with formatting</span><span class="nested">dolor</span></span></span>
</div>

person Daniel Lane    schedule 02.08.2016
comment
Интересно. Я признаю, что в конце концов я полностью переключился на другую структуру RTE, поэтому я больше не могу это напрямую тестировать. Я не помню, чтобы видел обработчиков, когда копался. Так что либо он был добавлен недавно, либо я его просто пропустил. Рад, что это сработало! Если вы дадите мне Plunkr / JsFiddle / какой-нибудь другой рабочий пример, я с радостью приму этот ответ, чтобы другим было легче его найти. - person Stephen Cohen; 03.08.2016
comment
@StephenCohen Я отредактировал свой ответ и предоставил фрагмент функционального кода. И, честно говоря, пропустить кастомные обработчики было несложно, мне пришлось копать несколько часов, чтобы с этим справиться. Quill - отличный проект, но документация местами нечеткая. - person Daniel Lane; 03.08.2016
comment
Отлично! Я вижу, что он все еще не идеален (если вы разрешите пользователю помещать произвольный контент в диапазон, его нельзя будет редактировать после применения формата), но он технически делает то, что я пытался сделать, и он работает Итак, вот ваша зеленая галочка! - person Stephen Cohen; 04.08.2016
comment
Я изо всех сил пытался найти способ вставить его в строку, и нашел ваш ответ. Оказывается, мне нужно было только изменить мой импорт с blots/block/embed на blots/embed. Что такое жизнь? - person Keno Clayton; 18.01.2017
comment
@KenoClayton Жизнь для таких людей, как мы, - это постоянный и непрерывный процесс превращения напитков с кофеином в странные и чудесные вещи, которые превращают волшебные устройства в колдовство. - person Daniel Lane; 05.04.2017
comment
Большое вам спасибо за ваши усилия! Очень полезный ответ. - person ffritz; 09.04.2019
comment
Спасибо, @DanielLane, за пример. Я реализовал очень похожий блот, но у меня возникали проблемы, когда при вставке всегда использовался новый блот (вместо простых абзацев). Оказывается, установка хорошего свойства [blot_name].className жизненно важна. В противном случае Quill слишком агрессивно интерпретирует вставленный текст как div или span. - person Josh; 10.01.2020
comment
@DanielLane благодарим вас за предоставленный код. Но проблема с этим кодом в том, что он создает круговую структуру и не позволяет ее сериализовать. если вы используете JSON.stringify(quill.getContents()), вы получите следующую ошибку: Преобразование круговой структуры в JSON - ›начало объекта с конструктором 'HTMLSpanElement' | свойство '__blot' - ›объект с конструктором 'Object' | свойство 'blot' - ›объект с конструктором 'Span' --- свойство 'domNode' замыкает круг - person Babak; 16.10.2020

Документация и руководства все еще пишутся, но стоит посмотреть, как реализованы существующие настраиваемые форматы. Формат формулы, в частности, очень похож на ваш вариант использования.

person jhchen    schedule 14.06.2016
comment
Я посмотрел на это и увидел ключевые отличия. Самая большая из которых формула - это встраивание, тогда как мне нужен встроенный блот, который может иметь нормальный дочерний контент. По сути, внешний диапазон отображает границу, первый дочерний диапазон отображает немного текста метаданных, а затем пользователь продолжает вводить текст, вводя его во второй дочерний диапазон. Я отредактирую свой вопрос с помощью изображения макета, которое может быть более четким. - person Stephen Cohen; 14.06.2016