@@ -10,16 +10,30 @@ import ko from "knockout";
10
10
import keyCodes from "Magento_Ui/js/lib/key-codes" ;
11
11
import _ from "underscore" ;
12
12
13
+ /**
14
+ * Strip HTML and return text
15
+ *
16
+ * @param {string } html
17
+ * @returns {string }
18
+ */
19
+ function stripHtml ( html : string ) {
20
+ const tempDiv = document . createElement ( "div" ) ;
21
+
22
+ tempDiv . innerHTML = html ;
23
+ return tempDiv . textContent ;
24
+ }
25
+
13
26
/**
14
27
* Add or remove the placeholder-text class from the element based on its content
15
28
*
16
29
* @param {Element } element
17
30
*/
18
31
function handlePlaceholderClass ( element : Element ) {
19
- if ( element . innerHTML . length === 0 ) {
20
- $ ( element ) . addClass ( "placeholder-text" ) ;
32
+ if ( stripHtml ( element . innerHTML ) . length === 0 ) {
33
+ element . innerHTML = "" ;
34
+ element . classList . add ( "placeholder-text" ) ;
21
35
} else {
22
- $ ( element ) . removeClass ( "placeholder-text" ) ;
36
+ element . classList . remove ( "placeholder-text" ) ;
23
37
}
24
38
}
25
39
@@ -29,31 +43,18 @@ ko.bindingHandlers.liveEdit = {
29
43
/**
30
44
* Init the live edit binding on an element
31
45
*
32
- * @param {any } element
46
+ * @param {HTMLElement } element
33
47
* @param {() => any } valueAccessor
34
48
* @param {KnockoutAllBindingsAccessor } allBindings
35
49
* @param {any } viewModel
36
50
* @param {KnockoutBindingContext } bindingContext
37
51
*/
38
- init ( element , valueAccessor , allBindings , viewModel , bindingContext ) {
52
+ init ( element : HTMLElement , valueAccessor , allBindings , viewModel , bindingContext ) {
39
53
const { field, placeholder, selectAll = false } = valueAccessor ( ) ;
40
54
let focusedValue = element . innerHTML ;
41
55
let previouslyFocused : boolean = false ;
42
56
let blurTimeout : number ;
43
57
44
- /**
45
- * Strip HTML and return text
46
- *
47
- * @param {string } html
48
- * @returns {string }
49
- */
50
- const stripHtml = ( html : string ) => {
51
- const tempDiv = document . createElement ( "div" ) ;
52
-
53
- tempDiv . innerHTML = html ;
54
- return tempDiv . textContent ;
55
- } ;
56
-
57
58
/**
58
59
* Record the value on focus, only conduct an update when data changes
59
60
*/
@@ -143,16 +144,47 @@ ko.bindingHandlers.liveEdit = {
143
144
handlePlaceholderClass ( element ) ;
144
145
} ;
145
146
147
+ /**
148
+ * On paste strip any HTML
149
+ */
150
+ const onPaste = ( ) => {
151
+ // Record the original caret position so we can ensure we restore it at the correct position
152
+ const selection = window . getSelection ( ) ;
153
+ const originalPositionStart = selection . getRangeAt ( 0 ) . cloneRange ( ) . startOffset ;
154
+ const originalPositionEnd = selection . getRangeAt ( 0 ) . cloneRange ( ) . endOffset ;
155
+ const originalContentLength = stripHtml ( element . innerHTML ) . length ;
156
+ // Allow the paste action to update the content
157
+ _ . defer ( ( ) => {
158
+ const strippedValue = stripHtml ( element . innerHTML ) ;
159
+ element . innerHTML = strippedValue ;
160
+ /**
161
+ * Calculate the position the caret should end up at, the difference in string length + the original
162
+ * end offset position
163
+ */
164
+ let restoredPosition = Math . abs ( strippedValue . length - originalContentLength ) + originalPositionStart ;
165
+ // If part of the text was selected adjust the position for the removed text
166
+ if ( originalPositionStart !== originalPositionEnd ) {
167
+ restoredPosition += Math . abs ( originalPositionEnd - originalPositionStart ) ;
168
+ }
169
+ const range = document . createRange ( ) ;
170
+ range . setStart ( element . childNodes [ 0 ] , restoredPosition ) ;
171
+ range . setEnd ( element . childNodes [ 0 ] , restoredPosition ) ;
172
+ selection . removeAllRanges ( ) ;
173
+ selection . addRange ( range ) ;
174
+ } ) ;
175
+ } ;
176
+
146
177
element . setAttribute ( "data-placeholder" , placeholder ) ;
147
178
element . textContent = viewModel . parent . dataStore . get ( field ) ;
148
- element . contentEditable = true ;
179
+ element . contentEditable = " true" ;
149
180
element . addEventListener ( "focus" , onFocus ) ;
150
181
element . addEventListener ( "blur" , onBlur ) ;
151
182
element . addEventListener ( "mousedown" , onMouseDown ) ;
152
183
element . addEventListener ( "keydown" , onKeyDown ) ;
153
184
element . addEventListener ( "keyup" , onKeyUp ) ;
154
185
element . addEventListener ( "input" , onInput ) ;
155
186
element . addEventListener ( "drop" , onDrop ) ;
187
+ element . addEventListener ( "paste" , onPaste ) ;
156
188
157
189
$ ( element ) . parent ( ) . css ( "cursor" , "text" ) ;
158
190
handlePlaceholderClass ( element ) ;
0 commit comments