mirror of https://github.com/sveltejs/svelte
feat: `bind:innerText` for `contenteditable` (#4291)
closes #3311 --------- Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>pull/8390/head
parent
a45afd5393
commit
f56fe33931
@ -0,0 +1,57 @@
|
|||||||
|
// Utilities for managing contenteditable nodes
|
||||||
|
import Attribute from '../nodes/Attribute';
|
||||||
|
import Element from '../nodes/Element';
|
||||||
|
|
||||||
|
export const CONTENTEDITABLE_BINDINGS = [
|
||||||
|
'textContent',
|
||||||
|
'innerHTML',
|
||||||
|
'innerText'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if node is an 'input' or 'textarea'.
|
||||||
|
* @param {Element} node The element to be checked
|
||||||
|
*/
|
||||||
|
function is_input_or_textarea(node: Element): boolean {
|
||||||
|
return node.name === 'textarea' || node.name === 'input';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given attribute is 'contenteditable'.
|
||||||
|
* @param {Attribute} attribute A node.attribute
|
||||||
|
*/
|
||||||
|
function is_attr_contenteditable(attribute: Attribute): boolean {
|
||||||
|
return attribute.name === 'contenteditable';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any of a node's attributes are 'contentenditable'.
|
||||||
|
* @param {Element} node The element to be checked
|
||||||
|
*/
|
||||||
|
export function has_contenteditable_attr(node: Element): boolean {
|
||||||
|
return node.attributes.some(is_attr_contenteditable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if node is not textarea or input, but has 'contenteditable' attribute.
|
||||||
|
* @param {Element} node The element to be tested
|
||||||
|
*/
|
||||||
|
export function is_contenteditable(node: Element): boolean {
|
||||||
|
return !is_input_or_textarea(node) && has_contenteditable_attr(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a given binding/node is contenteditable.
|
||||||
|
* @param {string} name A binding or node name to be checked
|
||||||
|
*/
|
||||||
|
export function is_name_contenteditable(name: string): boolean {
|
||||||
|
return CONTENTEDITABLE_BINDINGS.includes(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contenteditable attribute from the node (if it exists).
|
||||||
|
* @param {Element} node The element to get the attribute from
|
||||||
|
*/
|
||||||
|
export function get_contenteditable_attr(node: Element): Attribute | undefined {
|
||||||
|
return node.attributes.find(is_attr_contenteditable);
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
name: 'world'
|
||||||
|
},
|
||||||
|
|
||||||
|
ssrHtml: `
|
||||||
|
<editor contenteditable="true">world</editor>
|
||||||
|
<p>hello world</p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
async test({ assert, component, target, window }) {
|
||||||
|
// JSDom doesn't support innerText yet, so the test is not ideal
|
||||||
|
// https://github.com/jsdom/jsdom/issues/1245
|
||||||
|
const el = target.querySelector('editor');
|
||||||
|
assert.equal(el.innerText, 'world');
|
||||||
|
|
||||||
|
const event = new window.Event('input');
|
||||||
|
el.innerText = 'everybody';
|
||||||
|
await el.dispatchEvent(event);
|
||||||
|
assert.equal(component.name, 'everybody');
|
||||||
|
|
||||||
|
component.name = 'goodbye';
|
||||||
|
assert.equal(el.innerText, 'goodbye');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
<script>
|
||||||
|
export let name;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<editor contenteditable="true" bind:innerText={name} />
|
||||||
|
<p>hello {name}</p>
|
Loading…
Reference in new issue