pull/8394/head
baseballyama 4 years ago
parent aa147fc6f1
commit a128e3c56f

@ -174,6 +174,8 @@ export default class ElementWrapper extends Wrapper {
child_dynamic_element_block?: Block = null; child_dynamic_element_block?: Block = null;
child_dynamic_element?: ElementWrapper = null; child_dynamic_element?: ElementWrapper = null;
element_data_name = null;
constructor( constructor(
renderer: Renderer, renderer: Renderer,
block: Block, block: Block,
@ -287,6 +289,8 @@ export default class ElementWrapper extends Wrapper {
} }
this.fragment = new FragmentWrapper(renderer, block, node.children, this, strip_whitespace, next_sibling); this.fragment = new FragmentWrapper(renderer, block, node.children, this, strip_whitespace, next_sibling);
this.element_data_name = block.get_unique_name(`${this.var.name}_data`);
} }
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
@ -516,7 +520,8 @@ export default class ElementWrapper extends Wrapper {
child.render( child.render(
block, block,
is_template ? x`${node}.content` : node, is_template ? x`${node}.content` : node,
nodes nodes,
{ element_data_name: this.element_data_name }
); );
}); });
} }
@ -824,7 +829,6 @@ export default class ElementWrapper extends Wrapper {
add_spread_attributes(block: Block) { add_spread_attributes(block: Block) {
const levels = block.get_unique_name(`${this.var.name}_levels`); const levels = block.get_unique_name(`${this.var.name}_levels`);
const data = block.get_unique_name(`${this.var.name}_data`);
const initial_props = []; const initial_props = [];
const updates = []; const updates = [];
@ -855,9 +859,9 @@ export default class ElementWrapper extends Wrapper {
block.chunks.init.push(b` block.chunks.init.push(b`
let ${levels} = [${initial_props}]; let ${levels} = [${initial_props}];
let ${data} = {}; let ${this.element_data_name} = {};
for (let #i = 0; #i < ${levels}.length; #i += 1) { for (let #i = 0; #i < ${levels}.length; #i += 1) {
${data} = @assign(${data}, ${levels}[#i]); ${this.element_data_name} = @assign(${this.element_data_name}, ${levels}[#i]);
} }
`); `);
@ -868,13 +872,13 @@ export default class ElementWrapper extends Wrapper {
? x`@set_dynamic_element_data(${this.node.tag_expr.manipulate(block)})` ? x`@set_dynamic_element_data(${this.node.tag_expr.manipulate(block)})`
: x`@set_attributes`; : x`@set_attributes`;
block.chunks.hydrate.push( block.chunks.hydrate.unshift(
b`${fn}(${this.var}, ${data});` b`${fn}(${this.var}, ${this.element_data_name});`
); );
if (this.has_dynamic_attribute) { if (this.has_dynamic_attribute) {
block.chunks.update.push(b` block.chunks.update.unshift(b`
${fn}(${this.var}, ${data} = @get_spread_update(${levels}, [ ${fn}(${this.var}, ${this.element_data_name} = @get_spread_update(${levels}, [
${updates} ${updates}
])); ]));
`); `);
@ -890,23 +894,23 @@ export default class ElementWrapper extends Wrapper {
} }
block.chunks.mount.push(b` block.chunks.mount.push(b`
'value' in ${data} && (${data}.multiple ? @select_options : @select_option)(${this.var}, ${data}.value); 'value' in ${this.element_data_name} && (${this.element_data_name}.multiple ? @select_options : @select_option)(${this.var}, ${this.element_data_name}.value);
`); `);
block.chunks.update.push(b` block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(dependencies))} && 'value' in ${data}) (${data}.multiple ? @select_options : @select_option)(${this.var}, ${data}.value); if (${block.renderer.dirty(Array.from(dependencies))} && 'value' in ${this.element_data_name}) (${this.element_data_name}.multiple ? @select_options : @select_option)(${this.var}, ${this.element_data_name}.value);
`); `);
} else if (this.node.name === 'input' && this.attributes.find(attr => attr.node.name === 'value')) { } else if (this.node.name === 'input' && this.attributes.find(attr => attr.node.name === 'value')) {
const type = this.node.get_static_attribute_value('type'); const type = this.node.get_static_attribute_value('type');
if (type === null || type === '' || type === 'text' || type === 'email' || type === 'password') { if (type === null || type === '' || type === 'text' || type === 'email' || type === 'password') {
block.chunks.mount.push(b` block.chunks.mount.push(b`
if ('value' in ${data}) { if ('value' in ${this.element_data_name}) {
${this.var}.value = ${data}.value; ${this.var}.value = ${this.element_data_name}.value;
} }
`); `);
block.chunks.update.push(b` block.chunks.update.push(b`
if ('value' in ${data}) { if ('value' in ${this.element_data_name}) {
${this.var}.value = ${data}.value; ${this.var}.value = ${this.element_data_name}.value;
} }
`); `);
} }
@ -1220,8 +1224,8 @@ export default class ElementWrapper extends Wrapper {
if (this.dynamic_style_dependencies.size > 0) { if (this.dynamic_style_dependencies.size > 0) {
maybe_create_style_changed_var(); maybe_create_style_changed_var();
// If all dependencies are same as the style attribute dependencies, then we can skip the dirty check // If all dependencies are same as the style attribute dependencies, then we can skip the dirty check
condition = condition =
all_deps.size === this.dynamic_style_dependencies.size all_deps.size === this.dynamic_style_dependencies.size
? style_changed_var ? style_changed_var
: x`${style_changed_var} || ${condition}`; : x`${style_changed_var} || ${condition}`;
} }
@ -1232,7 +1236,6 @@ export default class ElementWrapper extends Wrapper {
} }
`); `);
} }
}); });
} }

@ -5,7 +5,9 @@ import Wrapper from './shared/Wrapper';
import MustacheTag from '../../nodes/MustacheTag'; import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag'; import RawMustacheTag from '../../nodes/RawMustacheTag';
import { x } from 'code-red'; import { x } from 'code-red';
import { Identifier } from 'estree'; import { Identifier, Expression } from 'estree';
import ElementWrapper from './Element';
import AttributeWrapper from './Element/Attribute';
export default class MustacheTagWrapper extends Tag { export default class MustacheTagWrapper extends Tag {
var: Identifier = { type: 'Identifier', name: 't' }; var: Identifier = { type: 'Identifier', name: 't' };
@ -14,10 +16,32 @@ export default class MustacheTagWrapper extends Tag {
super(renderer, block, parent, node); super(renderer, block, parent, node);
} }
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { render(block: Block, parent_node: Identifier, parent_nodes: Identifier, data: Record<string, unknown> | undefined) {
const contenteditable =
this.parent instanceof ElementWrapper &&
this.parent.attributes.filter((a) => a.node.name === 'contenteditable');
const spread_attres =
this.parent instanceof ElementWrapper &&
this.parent.attributes.filter((a) => a.node.is_spread);
let is_contenteditable: Expression | undefined = undefined;
if (contenteditable.length > 0) {
const value = (contenteditable[0] as AttributeWrapper).get_value(block);
is_contenteditable = x`~@contenteditable_truly_values.indexOf(${value})`;
} else if (spread_attres.length > 0 && data.element_data_name) {
is_contenteditable = x`~@contenteditable_truly_values.indexOf(${data.element_data_name}['contenteditable'])`;
}
const { init } = this.rename_this_method( const { init } = this.rename_this_method(
block, block,
value => x`@set_data(${this.var}, ${value})` value => {
if (is_contenteditable) {
return x`@set_data(${this.var}, ${value}, ${is_contenteditable})`;
} else {
return x`@set_data(${this.var}, ${value})`;
}
}
); );
block.add_element( block.add_element(

@ -85,7 +85,7 @@ export default class Wrapper {
); );
} }
render(_block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { render(_block: Block, _parent_node: Identifier, _parent_nodes: Identifier, _data: Record<string, any> = undefined) {
throw Error('Wrapper class is not renderable'); throw Error('Wrapper class is not renderable');
} }
} }

@ -83,17 +83,15 @@ export function dataset_dev(node: HTMLElement, property: string, value?: any) {
dispatch_dev('SvelteDOMSetDataset', { node, property, value }); dispatch_dev('SvelteDOMSetDataset', { node, property, value });
} }
export function set_data_dev(text, data) { export function set_data_dev(text: Text, data: unknown, is_contenteditable: boolean | undefined) {
data = '' + data; data = '' + data;
// In particular, when the property is contentEditable, ' 'TEXT_NODE is inserted into the text if (is_contenteditable) {
if (text.textContent === '' && text.wholeText !== '') {
if (text.wholeText === data) return; if (text.wholeText === data) return;
} else { } else {
if (text.textContent === data) return; if (text.data === data) return;
} }
dispatch_dev('SvelteDOMSetData', { node: text, data }); dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data; text.data = (data as string);
} }
export function validate_each_argument(arg) { export function validate_each_argument(arg) {

@ -581,15 +581,14 @@ export function claim_html_tag(nodes, is_svg: boolean) {
return new HtmlTagHydration(claimed_nodes, is_svg); return new HtmlTagHydration(claimed_nodes, is_svg);
} }
export function set_data(text, data) { export function set_data(text: Text, data: unknown, is_contenteditable: boolean | undefined) {
data = '' + data; data = '' + data;
// In particular, when the property is contentEditable, ' 'TEXT_NODE is inserted into the text if (is_contenteditable) {
if (text.textContent === '' && text.wholeText !== '') {
if (text.wholeText === data) return; if (text.wholeText === data) return;
} else { } else {
if (text.textContent === data) return; if (text.data === data) return;
} }
text.data = data; text.data = (data as string);
} }
export function set_input_value(input, value) { export function set_input_value(input, value) {

@ -194,3 +194,5 @@ export function split_css_unit(value: number | string): [number, string] {
const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/); const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/);
return split ? [parseFloat(split[1]), split[2] || 'px'] : [value as number, 'px']; return split ? [parseFloat(split[1]), split[2] || 'px'] : [value as number, 'px'];
} }
export const contenteditable_truly_values = ['', true, 1, 'true', 'contenteditable'];

@ -1,7 +1,5 @@
export default { export default {
html: ` html: '<div contenteditable=""></div>',
<div contenteditable=""></div>
`,
async test({ assert, target, window }) { async test({ assert, target, window }) {
const div = target.querySelector('div'); const div = target.querySelector('div');
@ -9,7 +7,6 @@ export default {
div.insertBefore(text, null); div.insertBefore(text, null);
const event = new window.InputEvent('input'); const event = new window.InputEvent('input');
await div.dispatchEvent(event); await div.dispatchEvent(event);
assert.equal(div.textContent, 'a'); assert.equal(div.textContent, 'a');
} }
}; };

@ -0,0 +1,12 @@
export default {
html: '<div contenteditable="true"></div>',
async test({ assert, target, window }) {
const div = target.querySelector('div');
const text = window.document.createTextNode('a');
div.insertBefore(text, null);
const event = new window.InputEvent('input');
await div.dispatchEvent(event);
assert.equal(div.textContent, 'a');
}
};

@ -0,0 +1,6 @@
<script>
let text = '';
const updater = (event) => {text = event.target.textContent}
</script>
<div contenteditable="true" on:input={updater}>{text}</div>

@ -0,0 +1,13 @@
export default {
html: '<div contenteditable="true"></div>',
ssrHtml: '<div contenteditable=""></div>',
async test({ assert, target, window }) {
const div = target.querySelector('div');
const text = window.document.createTextNode('a');
div.insertBefore(text, null);
const event = new window.InputEvent('input');
await div.dispatchEvent(event);
assert.equal(div.textContent, 'a');
}
};

@ -0,0 +1,9 @@
<script>
let text = '';
const updater = (event) => {text = event.target.textContent}
$: spread = {
contenteditable: text !== undefined,
}
</script>
<div {...spread} on:input={updater}>{text}</div>

@ -0,0 +1,12 @@
export default {
html: '<div contenteditable="false"></div>',
async test({ assert, target, component, window }) {
const div = target.querySelector('div');
const text = window.document.createTextNode('a');
div.insertBefore(text, null);
assert.equal(div.textContent, 'a');
component.text = 'bcde';
assert.equal(div.textContent, 'bcdea');
}
};

@ -0,0 +1,6 @@
<script>
export let text = '';
const updater = (event) => {text = event.target.textContent}
</script>
<div contenteditable="false" on:input={updater}>{text}</div>

@ -1,6 +1,6 @@
export default { export default {
html:'<div>same text</div>', html:'<div>same text</div>',
async test({ assert, component, target }) { async test({ assert, target }) {
await new Promise(f => setTimeout(f, 10)); await new Promise(f => setTimeout(f, 10));
assert.htmlEqual(target.innerHTML, ` assert.htmlEqual(target.innerHTML, `
<div>same text text</div> <div>same text text</div>

@ -1,8 +1,8 @@
<script> <script>
let text = 'same'; let text = 'same';
setTimeout(()=>{ setTimeout(() => {
text = 'same text' text = 'same text';
},5) }, 5);
</script> </script>
<div>{text} text</div> <div>{text} text</div>

Loading…
Cancel
Save