-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support beforeinput event. #8411
Changes from 11 commits
f63d9ab
349150b
190a16d
8ede3f7
b2b60ed
2566d91
6be3f37
c163188
0d655cb
63512ba
752daa1
f5186a7
6d98c74
a831c7f
a0b70ab
20664dd
e47dcba
74f7712
3f62bf5
4a60b82
4f99489
5b144ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<title>Slatejs App</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script>!function(e){function r(r){for(var n,l,f=r[0],i=r[1],a=r[2],c=0,s=[];c<f.length;c++)l=f[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,f=1;f<t.length;f++){var i=t[f];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var f=this.webpackJsonp7088=this.webpackJsonp7088||[],i=f.push.bind(f);f.push=r,f=f.slice();for(var a=0;a<f.length;a++)r(f[a]);var p=i;t()}([])</script> | ||
<!-- Compiled and minified version of https://www.slatejs.org/examples/plaintext --> | ||
<script src="issue-7088.js"></script> | ||
</body> | ||
</html> |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/// <reference types="cypress" /> | ||
|
||
describe('slatejs', () => { | ||
it('test', () => { | ||
cy.visit('fixtures/issue-7088.html') | ||
|
||
const testString = 'Hello world' | ||
|
||
cy.get('[data-slate-editor="true"]') | ||
.type(testString, { noUpdate: true }) | ||
|
||
cy.contains('[data-slate-string="true"]', testString) | ||
.should('be.visible') | ||
|
||
cy.get('[data-slate-editor="true"]') | ||
.type('{ctrl}{shift}{backspace}', { release: false, noUpdate: true }) | ||
|
||
cy.get('span[contenteditable="false"]').should('have.text', 'Enter some plain text...') | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,7 @@ export type KeyEventType = | |
| 'keypress' | ||
| 'input' | ||
| 'textInput' | ||
| 'beforeinput' | ||
|
||
const toModifiersEventOptions = (modifiers: KeyboardModifiers) => { | ||
return { | ||
|
@@ -489,7 +490,7 @@ function _getEndIndex (str, substr) { | |
const simulatedDefaultKeyMap: { [key: string]: SimulatedDefault } = { | ||
Enter: (el, key, options) => { | ||
// if input element, Enter key does not insert text | ||
if (!$elements.isInput(el)) { | ||
if (!options.noUpdate && !$elements.isInput(el)) { | ||
$selection.replaceSelectionContents(el, '\n') | ||
} | ||
|
||
|
@@ -593,6 +594,7 @@ export interface typeOptions { | |
release?: boolean | ||
_log?: any | ||
delay?: number | ||
noUpdate?: boolean | ||
onError?: Function | ||
onEvent?: Function | ||
onBeforeEvent?: Function | ||
|
@@ -801,6 +803,7 @@ export class Keyboard { | |
let eventConstructor = 'KeyboardEvent' | ||
let cancelable = true | ||
let addModifiers = true | ||
let inputType = '' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this will add the I'll take a look There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right. It should be written in that way. |
||
|
||
switch (eventType) { | ||
case 'keydown': | ||
|
@@ -832,6 +835,14 @@ export class Keyboard { | |
data = text | ||
break | ||
|
||
case 'beforeinput': | ||
eventConstructor = 'InputEvent' | ||
addModifiers = false | ||
data = text | ||
location = undefined | ||
cancelable = false | ||
inputType = this.getInputType(keyDetails.code) | ||
break | ||
case 'input': | ||
eventConstructor = 'InputEvent' | ||
addModifiers = false | ||
|
@@ -875,6 +886,7 @@ export class Keyboard { | |
data, | ||
detail: 0, | ||
view: win, | ||
inputType, | ||
}, | ||
_.isUndefined, | ||
), | ||
|
@@ -902,8 +914,8 @@ export class Keyboard { | |
} else { | ||
// For some reason we can't set certain props on Keyboard Events in chrome < 63. | ||
// So we'll use the plain Event constructor | ||
// event = new win[eventConstructor](eventType, eventOptions) | ||
event = new win['Event'](eventType, eventOptions) | ||
event = new win[eventConstructor](eventType, eventOptions) | ||
// event = new win['Event'](eventType, eventOptions) | ||
_.extend(event, eventOptions) | ||
} | ||
|
||
|
@@ -917,6 +929,56 @@ export class Keyboard { | |
return dispatched | ||
} | ||
|
||
getInputType (code) { | ||
const { shift, ctrl } = this.getActiveModifiers() | ||
|
||
if (shift && ctrl && code === 'Delete') { | ||
return 'deleteHardLineForward' | ||
} | ||
|
||
if (ctrl && code === 'Delete') { | ||
return 'deleteWordForward' | ||
} | ||
|
||
if (code === 'Delete') { | ||
return 'deleteContentForward' | ||
} | ||
|
||
if (shift && ctrl && code === 'Backspace') { | ||
return 'deleteHardLineBackward' | ||
} | ||
|
||
if (ctrl && code === 'Backspace') { | ||
return 'deleteWordBackward' | ||
} | ||
|
||
if (code === 'Backspace') { | ||
return 'deleteContentBackward' | ||
} | ||
|
||
if (code === 'Enter') { | ||
return 'insertLineBreak' | ||
} | ||
|
||
if (ctrl && code === 'KeyV') { | ||
return 'insertFromPaste' | ||
} | ||
|
||
if (ctrl && code === 'KeyX') { | ||
return 'deleteByCut' | ||
} | ||
|
||
if (ctrl && code === 'KeyZ') { | ||
return 'historyUndo' | ||
} | ||
|
||
if (ctrl && code === 'KeyY') { | ||
return 'historyRedo' | ||
} | ||
|
||
return 'insertText' | ||
} | ||
|
||
getActiveModifiers () { | ||
return _.clone(this.state('keyboardModifiers')) || _.clone(INITIAL_MODIFIERS) | ||
} | ||
|
@@ -982,9 +1044,14 @@ export class Keyboard { | |
if (!key.text) { | ||
key.events.keypress = false | ||
key.events.textInput = false | ||
key.events.beforeinput = false | ||
if (key.key !== 'Backspace' && key.key !== 'Delete') { | ||
key.events.input = false | ||
} | ||
|
||
if (key.key === 'Backspace' || key.key === 'Delete') { | ||
key.events.beforeinput = true | ||
} | ||
} | ||
|
||
let elToType | ||
|
@@ -1018,10 +1085,15 @@ export class Keyboard { | |
this.fireSimulatedEvent(elToType, 'keypress', key, options) | ||
) { | ||
if ( | ||
shouldIgnoreEvent('textInput', key.events) || | ||
this.fireSimulatedEvent(elToType, 'textInput', key, options) | ||
shouldIgnoreEvent('beforeinput', key.events) || | ||
this.fireSimulatedEvent(elToType, 'beforeinput', key, options) | ||
) { | ||
return this.performSimulatedDefault(elToType, key, options) | ||
if ( | ||
shouldIgnoreEvent('textInput', key.events) || | ||
this.fireSimulatedEvent(elToType, 'textInput', key, options) | ||
) { | ||
return this.performSimulatedDefault(elToType, key, options) | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -1082,10 +1154,12 @@ export class Keyboard { | |
return | ||
} | ||
|
||
// noop if not in a text-editable | ||
const ret = $selection.replaceSelectionContents(el, key.text) | ||
if (!options.noUpdate) { | ||
// noop if not in a text-editable | ||
const ret = $selection.replaceSelectionContents(el, key.text) | ||
|
||
debug('replaceSelectionContents:', key.text, ret) | ||
debug('replaceSelectionContents:', key.text, ret) | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried removing the noUpdate options here and the test still passed, is there a test case that fails w/o the option?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right.
noUpdate
isn't necessary.I added it because there were some cases when
cy.type()
breaks the html structure inside slatejs. It seems that it was becausebeforeinput
part was buggy.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the reason you previously needed noUpdate was due to the event not being set as cancelable https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event, fixed now so we will respect a cancelled event and not perform the default action