fable-editor 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2027,6 +2027,8 @@ class FableEditor {
2027
2027
  }
2028
2028
  if (e.key === '`' && !e.ctrlKey && !e.metaKey && !e.altKey)
2029
2029
  this.tryCodeTyping(e);
2030
+ if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey)
2031
+ this.tryExitBlockquote(e);
2030
2032
  });
2031
2033
  this.on(this.ed, 'paste', (e) => this.handlePaste(e));
2032
2034
  this.on(this.ed, 'drop', (e) => {
@@ -2369,6 +2371,32 @@ class FableEditor {
2369
2371
  }
2370
2372
  return null;
2371
2373
  }
2374
+ /** Enter on an empty last line inside a blockquote exits it into a plain paragraph, mirroring how lists exit on an empty item. */
2375
+ tryExitBlockquote(e) {
2376
+ const s = window.getSelection();
2377
+ if (!s || !s.isCollapsed || !s.anchorNode || !this.ed.contains(s.anchorNode))
2378
+ return;
2379
+ const block = this.closestBlock(s.anchorNode);
2380
+ if (!block || block.tagName === 'BLOCKQUOTE' || (block.textContent || '').trim() !== '')
2381
+ return;
2382
+ const bq = block.closest('blockquote');
2383
+ if (!bq || !this.ed.contains(bq) || block !== bq.lastElementChild)
2384
+ return;
2385
+ e.preventDefault();
2386
+ const p = document.createElement('p');
2387
+ p.appendChild(document.createElement('br'));
2388
+ bq.after(p);
2389
+ block.remove();
2390
+ if (!bq.firstElementChild)
2391
+ bq.remove();
2392
+ const r = document.createRange();
2393
+ r.setStart(p, 0);
2394
+ r.collapse(true);
2395
+ s.removeAllRanges();
2396
+ s.addRange(r);
2397
+ this.saveSel();
2398
+ this.onChange();
2399
+ }
2372
2400
  selectedBlocks() {
2373
2401
  const s = window.getSelection();
2374
2402
  if (!s || !s.rangeCount || !this.ed.contains(s.anchorNode))
@@ -5560,7 +5588,27 @@ class FableEditor {
5560
5588
  }
5561
5589
  /* ---------------------------------------------------------- selection bubble toolbar */
5562
5590
  toggleBlock(tag) {
5563
- this.exec('formatBlock', this.currentBlock() === tag ? '<p>' : '<' + tag + '>');
5591
+ const active = this.currentBlock() === tag;
5592
+ this.exec('formatBlock', active ? '<p>' : '<' + tag + '>');
5593
+ if (tag === 'blockquote' && active)
5594
+ this.unwrapBlockquote();
5595
+ }
5596
+ /** Some browsers leave the <blockquote> wrapper in place after formatBlock('<p>'); strip it manually if so. */
5597
+ unwrapBlockquote() {
5598
+ const s = window.getSelection();
5599
+ let node = s && this.ed.contains(s.anchorNode) ? s.anchorNode : null;
5600
+ while (node && node !== this.ed) {
5601
+ if (node.nodeType === 1 && node.tagName === 'BLOCKQUOTE') {
5602
+ const bq = node;
5603
+ const parent = bq.parentNode;
5604
+ while (bq.firstChild)
5605
+ parent.insertBefore(bq.firstChild, bq);
5606
+ parent.removeChild(bq);
5607
+ this.onChange();
5608
+ return;
5609
+ }
5610
+ node = node.parentNode;
5611
+ }
5564
5612
  }
5565
5613
  clearSelToolbar() {
5566
5614
  this.selCtx?.remove();
@@ -5627,6 +5675,12 @@ class FableEditor {
5627
5675
  const s = window.getSelection();
5628
5676
  if (!s || !s.anchorNode || !this.ed.contains(s.anchorNode))
5629
5677
  return 'p';
5678
+ let node = s.anchorNode;
5679
+ while (node && node !== this.ed) {
5680
+ if (node.nodeType === 1 && node.tagName === 'BLOCKQUOTE')
5681
+ return 'blockquote';
5682
+ node = node.parentNode;
5683
+ }
5630
5684
  const b = this.closestBlock(s.anchorNode);
5631
5685
  return b ? b.tagName.toLowerCase().replace('div', 'p') : 'p';
5632
5686
  }