autopair 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.
- package/cypress/e2e/autopair.cy.js +33 -26
- package/package.json +1 -1
- package/src/autopair.js +32 -27
|
@@ -93,6 +93,7 @@ describe('autopair', () => {
|
|
|
93
93
|
cy.get('textarea').then($el => {
|
|
94
94
|
expect($el.val()).to.eq('();');
|
|
95
95
|
expect($el[0].selectionStart).to.eq(1);
|
|
96
|
+
expect($el[0].selectionEnd).to.eq(1);
|
|
96
97
|
});
|
|
97
98
|
});
|
|
98
99
|
|
|
@@ -115,6 +116,7 @@ describe('autopair', () => {
|
|
|
115
116
|
cy.get('textarea').then($el => {
|
|
116
117
|
expect($el.val()).to.eq('(.');
|
|
117
118
|
expect($el[0].selectionStart).to.eq(1);
|
|
119
|
+
expect($el[0].selectionEnd).to.eq(1);
|
|
118
120
|
});
|
|
119
121
|
});
|
|
120
122
|
|
|
@@ -128,6 +130,7 @@ describe('autopair', () => {
|
|
|
128
130
|
cy.get('textarea').then($el => {
|
|
129
131
|
expect($el.val()).to.eq('((()))');
|
|
130
132
|
expect($el[0].selectionStart).to.eq(3); // cursor inside the innermost pair
|
|
133
|
+
expect($el[0].selectionEnd).to.eq(3);
|
|
131
134
|
});
|
|
132
135
|
});
|
|
133
136
|
|
|
@@ -141,6 +144,7 @@ describe('autopair', () => {
|
|
|
141
144
|
cy.get('textarea').then($el => {
|
|
142
145
|
expect($el.val()).to.eq("''");
|
|
143
146
|
expect($el[0].selectionStart).to.eq(1); // cursor inside
|
|
147
|
+
expect($el[0].selectionEnd).to.eq(1);
|
|
144
148
|
});
|
|
145
149
|
|
|
146
150
|
// Move cursor to the end
|
|
@@ -156,37 +160,40 @@ describe('autopair', () => {
|
|
|
156
160
|
cy.get('textarea').then($el => {
|
|
157
161
|
expect($el.val()).to.eq("'''");
|
|
158
162
|
expect($el[0].selectionStart).to.eq(3); // cursor after last '
|
|
163
|
+
expect($el[0].selectionEnd).to.eq(3);
|
|
159
164
|
});
|
|
160
165
|
});
|
|
161
166
|
|
|
162
|
-
it('
|
|
167
|
+
it('autopairs existing custom quotes “ and ”', () => {
|
|
163
168
|
cy.visit('/index.html');
|
|
164
169
|
|
|
165
|
-
//
|
|
166
|
-
cy.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
170
|
+
// Type opening custom quote
|
|
171
|
+
cy.get('textarea').type('“');
|
|
172
|
+
|
|
173
|
+
// Should autopair to “”
|
|
174
|
+
cy.get('textarea').then($el => {
|
|
175
|
+
expect($el.val()).to.eq('“”');
|
|
176
|
+
expect($el[0].selectionStart).to.eq(1); // cursor inside
|
|
177
|
+
expect($el[0].selectionEnd).to.eq(1);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('supports undo and redo after typing a pair', () => {
|
|
182
|
+
cy.visit('/index.html');
|
|
183
|
+
|
|
184
|
+
// Type a pair
|
|
185
|
+
cy.get('textarea').type('()');
|
|
186
|
+
|
|
187
|
+
// Undo
|
|
188
|
+
cy.get('textarea').then($el => {
|
|
189
|
+
$el[0].ownerDocument.execCommand('undo');
|
|
190
|
+
});
|
|
191
|
+
cy.get('textarea').should('have.value', '');
|
|
192
|
+
|
|
193
|
+
// Redo
|
|
194
|
+
cy.get('textarea').then($el => {
|
|
195
|
+
$el[0].ownerDocument.execCommand('redo');
|
|
190
196
|
});
|
|
197
|
+
cy.get('textarea').should('have.value', '()');
|
|
191
198
|
});
|
|
192
199
|
});
|
package/package.json
CHANGED
package/src/autopair.js
CHANGED
|
@@ -5,32 +5,38 @@ export default function autopair(textarea, pairs = {
|
|
|
5
5
|
"'": "'",
|
|
6
6
|
'"': '"'
|
|
7
7
|
}) {
|
|
8
|
+
let openings = Object.keys(pairs);
|
|
9
|
+
let closings = Object.values(pairs);
|
|
10
|
+
|
|
11
|
+
let insertText = text => document.execCommand('insertText', false, text);
|
|
12
|
+
let setCursor = pos => {
|
|
13
|
+
textarea.selectionStart = textarea.selectionEnd = pos;
|
|
14
|
+
};
|
|
15
|
+
|
|
8
16
|
let handler = evt => {
|
|
9
|
-
|
|
17
|
+
let { selectionStart: start, selectionEnd: end, value } = textarea;
|
|
10
18
|
|
|
11
19
|
// Typethrough
|
|
12
|
-
if (start === end) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (isClosing && next === evt.key) {
|
|
17
|
-
evt.preventDefault();
|
|
18
|
-
textarea.selectionStart = textarea.selectionEnd = end + 1;
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
20
|
+
if (start === end && closings.includes(evt.key) && value[end] === evt.key) {
|
|
21
|
+
evt.preventDefault();
|
|
22
|
+
setCursor(end + 1);
|
|
23
|
+
return;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
// Handle backspace inside a direct pair
|
|
24
27
|
if (evt.key === 'Backspace' && start === end && start > 0) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
let left = value[start - 1];
|
|
29
|
+
let right = value[start];
|
|
30
|
+
let opening = openings.find(k => pairs[k] === right);
|
|
31
|
+
|
|
28
32
|
if (left === opening) {
|
|
29
33
|
evt.preventDefault();
|
|
34
|
+
|
|
30
35
|
// Select the pair and delete in one go
|
|
31
36
|
textarea.selectionStart = start - 1;
|
|
32
37
|
textarea.selectionEnd = start + 1;
|
|
33
|
-
|
|
38
|
+
|
|
39
|
+
insertText('');
|
|
34
40
|
|
|
35
41
|
return;
|
|
36
42
|
}
|
|
@@ -38,7 +44,7 @@ export default function autopair(textarea, pairs = {
|
|
|
38
44
|
return; // normal backspace
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
let closing = pairs[evt.key];
|
|
42
48
|
if (!closing) return;
|
|
43
49
|
|
|
44
50
|
// Wrap selection if present
|
|
@@ -46,18 +52,17 @@ export default function autopair(textarea, pairs = {
|
|
|
46
52
|
evt.preventDefault();
|
|
47
53
|
textarea.selectionStart = start;
|
|
48
54
|
textarea.selectionEnd = end;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
textarea.selectionEnd = end + 1;
|
|
55
|
+
insertText(evt.key + value.slice(start, end) + closing);
|
|
56
|
+
setCursor(start + 1);
|
|
52
57
|
return;
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
let nextCharWhitelist = /[\s;})\]]/;
|
|
61
|
+
let nextChar = value[end] || '';
|
|
62
|
+
let prevChar = value[start - 1] || '';
|
|
63
|
+
let insidePair = closing === nextChar;
|
|
64
|
+
let safeNext = nextChar === '' || nextCharWhitelist.test(nextChar);
|
|
65
|
+
let isSymmetric = evt.key === closing;
|
|
61
66
|
|
|
62
67
|
// Autoclose only when allowed
|
|
63
68
|
if (!insidePair && (!safeNext || (isSymmetric && start === end && prevChar === evt.key))) {
|
|
@@ -65,9 +70,9 @@ export default function autopair(textarea, pairs = {
|
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
evt.preventDefault();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
|
|
74
|
+
insertText(evt.key + closing);
|
|
75
|
+
setCursor(start + 1);
|
|
71
76
|
};
|
|
72
77
|
|
|
73
78
|
textarea.addEventListener('keydown', handler);
|