@@ -8,10 +8,25 @@ import { EmbedBlot, type ScrollBlot } from "parchment";
88import Quill , { Delta } from "quill" ;
99import Clipboard , { matchNewline } from "quill/modules/clipboard" ;
1010
11- function isLine ( node : Node , scroll : ScrollBlot ) : boolean {
11+ export default class CustomClipboard extends Clipboard {
12+ constructor ( quill : Quill , options : any ) {
13+ super ( quill , options ) ;
14+ // remove default CLIPBOARD_CONFIG list matchers for ol and ul
15+ // https://github.com/slab/quill/blob/539cbffd0a13b18e9c65eb84dd35e6596e403158/packages/quill/src/modules/clipboard.ts#L32
16+ this . matchers = this . matchers . filter ( matcher => matcher [ 0 ] !== "ol, ul" && matcher [ 0 ] !== Node . TEXT_NODE ) ;
17+ // adding back, we do not actually want to remove newline matching
18+ this . matchers . unshift ( [ Node . TEXT_NODE , matchNewline ] ) ;
19+ // add custom text matcher to better handle spaces and newlines
20+ this . matchers . unshift ( [ Node . TEXT_NODE , customMatchText ] ) ;
21+ // add custom list matchers for ol and ul to allow custom list types (lower-alpha, lower-roman, etc.)
22+ this . addMatcher ( "ol, ul" , matchList ) ;
23+ }
24+ }
25+
26+ function isLine ( node : Node , scroll : ScrollBlot ) : any {
1227 if ( ! ( node instanceof Element ) ) return false ;
1328 const match = scroll . query ( node ) ;
14- // @ts -expect-error prototype does not exist on Blot
29+ // @ts -expect-error prototype not exist on match
1530 if ( match && match . prototype instanceof EmbedBlot ) return false ;
1631
1732 return [
@@ -62,10 +77,10 @@ function isBetweenInlineElements(node: HTMLElement, scroll: ScrollBlot): boolean
6277}
6378
6479const preNodes = new WeakMap ( ) ;
65- function isPre ( node : Node | null ) : any {
80+ function isPre ( node : Node | null ) : boolean {
6681 if ( node == null ) return false ;
6782 if ( ! preNodes . has ( node ) ) {
68- // @ts -expect-error tagName does not exist on Node
83+ // @ts -expect-error tagName not exist on Node
6984 if ( node . tagName === "PRE" ) {
7085 preNodes . set ( node , true ) ;
7186 } else {
@@ -75,10 +90,8 @@ function isPre(node: Node | null): any {
7590 return preNodes . get ( node ) ;
7691}
7792
78- // overrides matchText from Quill's Clipboard module
79- // removing text replacements that interfere with adding \t (tab)
80- function matchText ( node : HTMLElement , delta : Delta , scroll : ScrollBlot ) : Delta {
81- // @ts -expect-error data does not exist on HTMLElement
93+ function customMatchText ( node : HTMLElement , delta : Delta , scroll : ScrollBlot ) : Delta {
94+ // @ts -expect-error data not exist on node
8295 let text = node . data as string ;
8396 // Word represents empty line with <o:p> </o:p>
8497 if ( node . parentElement ?. tagName === "O:P" ) {
@@ -88,6 +101,7 @@ function matchText(node: HTMLElement, delta: Delta, scroll: ScrollBlot): Delta {
88101 if ( text . trim ( ) . length === 0 && text . includes ( "\n" ) && ! isBetweenInlineElements ( node , scroll ) ) {
89102 return delta ;
90103 }
104+ // collapse consecutive spaces into one
91105 text = text . replace ( / { 2 , } / g, " " ) ;
92106 if (
93107 ( node . nextSibling == null && node . parentElement != null && isLine ( node . parentElement , scroll ) ) ||
@@ -100,7 +114,7 @@ function matchText(node: HTMLElement, delta: Delta, scroll: ScrollBlot): Delta {
100114 return delta . insert ( text ) ;
101115}
102116
103- function matchList ( node : HTMLElement , delta : Delta , _scroll : ScrollBlot ) : Delta {
117+ function matchList ( node : HTMLElement , delta : Delta ) : Delta {
104118 const format = "list" ;
105119 let list = "ordered" ;
106120 const element = node as HTMLUListElement ;
@@ -131,20 +145,6 @@ function matchList(node: HTMLElement, delta: Delta, _scroll: ScrollBlot): Delta
131145 return newDelta . push ( op ) ;
132146 }
133147 const formats = list ? { [ format ] : list } : { } ;
134-
135148 return newDelta . insert ( op . insert , { ...formats , ...op . attributes } ) ;
136149 } , new Delta ( ) ) ;
137150}
138-
139- export default class CustomClipboard extends Clipboard {
140- constructor ( quill : Quill , options : any ) {
141- super ( quill , options ) ;
142-
143- // remove default list matchers for ol and ul
144- this . matchers = this . matchers . filter ( matcher => matcher [ 0 ] !== "ol, ul" && matcher [ 0 ] !== Node . TEXT_NODE ) ;
145- this . addMatcher ( Node . TEXT_NODE , matchNewline ) ;
146- this . addMatcher ( Node . TEXT_NODE , matchText ) ;
147- // add custom list matchers for ol and ul to allow custom list types (lower-alpha, lower-roman, etc.)
148- this . addMatcher ( "ol, ul" , matchList ) ;
149- }
150- }
0 commit comments