block.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import extend from 'extend';
  2. import Delta from 'quill-delta';
  3. import Parchment from 'parchment';
  4. import Break from './break';
  5. import Inline from './inline';
  6. import TextBlot from './text';
  7. const NEWLINE_LENGTH = 1;
  8. class BlockEmbed extends Parchment.Embed {
  9. attach() {
  10. super.attach();
  11. this.attributes = new Parchment.Attributor.Store(this.domNode);
  12. }
  13. delta() {
  14. return new Delta().insert(this.value(), extend(this.formats(), this.attributes.values()));
  15. }
  16. format(name, value) {
  17. let attribute = Parchment.query(name, Parchment.Scope.BLOCK_ATTRIBUTE);
  18. if (attribute != null) {
  19. this.attributes.attribute(attribute, value);
  20. }
  21. }
  22. formatAt(index, length, name, value) {
  23. this.format(name, value);
  24. }
  25. insertAt(index, value, def) {
  26. if (typeof value === 'string' && value.endsWith('\n')) {
  27. let block = Parchment.create(Block.blotName);
  28. this.parent.insertBefore(block, index === 0 ? this : this.next);
  29. block.insertAt(0, value.slice(0, -1));
  30. } else {
  31. super.insertAt(index, value, def);
  32. }
  33. }
  34. }
  35. BlockEmbed.scope = Parchment.Scope.BLOCK_BLOT;
  36. // It is important for cursor behavior BlockEmbeds use tags that are block level elements
  37. class Block extends Parchment.Block {
  38. constructor(domNode) {
  39. super(domNode);
  40. this.cache = {};
  41. }
  42. delta() {
  43. if (this.cache.delta == null) {
  44. this.cache.delta = this.descendants(Parchment.Leaf).reduce((delta, leaf) => {
  45. if (leaf.length() === 0) {
  46. return delta;
  47. } else {
  48. return delta.insert(leaf.value(), bubbleFormats(leaf));
  49. }
  50. }, new Delta()).insert('\n', bubbleFormats(this));
  51. }
  52. return this.cache.delta;
  53. }
  54. deleteAt(index, length) {
  55. super.deleteAt(index, length);
  56. this.cache = {};
  57. }
  58. formatAt(index, length, name, value) {
  59. if (length <= 0) return;
  60. if (Parchment.query(name, Parchment.Scope.BLOCK)) {
  61. if (index + length === this.length()) {
  62. this.format(name, value);
  63. }
  64. } else {
  65. super.formatAt(index, Math.min(length, this.length() - index - 1), name, value);
  66. }
  67. this.cache = {};
  68. }
  69. insertAt(index, value, def) {
  70. if (def != null) return super.insertAt(index, value, def);
  71. if (value.length === 0) return;
  72. let lines = value.split('\n');
  73. let text = lines.shift();
  74. if (text.length > 0) {
  75. if (index < this.length() - 1 || this.children.tail == null) {
  76. super.insertAt(Math.min(index, this.length() - 1), text);
  77. } else {
  78. this.children.tail.insertAt(this.children.tail.length(), text);
  79. }
  80. this.cache = {};
  81. }
  82. let block = this;
  83. lines.reduce(function(index, line) {
  84. block = block.split(index, true);
  85. block.insertAt(0, line);
  86. return line.length;
  87. }, index + text.length);
  88. }
  89. insertBefore(blot, ref) {
  90. let head = this.children.head;
  91. super.insertBefore(blot, ref);
  92. if (head instanceof Break) {
  93. head.remove();
  94. }
  95. this.cache = {};
  96. }
  97. length() {
  98. if (this.cache.length == null) {
  99. this.cache.length = super.length() + NEWLINE_LENGTH;
  100. }
  101. return this.cache.length;
  102. }
  103. moveChildren(target, ref) {
  104. super.moveChildren(target, ref);
  105. this.cache = {};
  106. }
  107. optimize(context) {
  108. super.optimize(context);
  109. this.cache = {};
  110. }
  111. path(index) {
  112. return super.path(index, true);
  113. }
  114. removeChild(child) {
  115. super.removeChild(child);
  116. this.cache = {};
  117. }
  118. split(index, force = false) {
  119. if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {
  120. let clone = this.clone();
  121. if (index === 0) {
  122. this.parent.insertBefore(clone, this);
  123. return this;
  124. } else {
  125. this.parent.insertBefore(clone, this.next);
  126. return clone;
  127. }
  128. } else {
  129. let next = super.split(index, force);
  130. this.cache = {};
  131. return next;
  132. }
  133. }
  134. }
  135. Block.blotName = 'block';
  136. Block.tagName = 'P';
  137. Block.defaultChild = 'break';
  138. Block.allowedChildren = [Inline, Parchment.Embed, TextBlot];
  139. function bubbleFormats(blot, formats = {}) {
  140. if (blot == null) return formats;
  141. if (typeof blot.formats === 'function') {
  142. formats = extend(formats, blot.formats());
  143. }
  144. if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {
  145. return formats;
  146. }
  147. return bubbleFormats(blot.parent, formats);
  148. }
  149. export { bubbleFormats, BlockEmbed, Block as default };