input value default

pull/5239/head
asvsfs 5 years ago
parent f4f16da455
commit fac15deb81

@ -1,13 +1,13 @@
import Attribute from '../../../nodes/Attribute'; import Attribute from "../../../nodes/Attribute";
import Block from '../../Block'; import Block from "../../Block";
import fix_attribute_casing from './fix_attribute_casing'; import fix_attribute_casing from "./fix_attribute_casing";
import ElementWrapper from './index'; import ElementWrapper from "./index";
import { string_literal } from '../../../utils/stringify'; import { string_literal } from "../../../utils/stringify";
import { b, x } from 'code-red'; import { b, x } from "code-red";
import Expression from '../../../nodes/shared/Expression'; import Expression from "../../../nodes/shared/Expression";
import Text from '../../../nodes/Text'; import Text from "../../../nodes/Text";
import handle_select_value_binding from './handle_select_value_binding'; import handle_select_value_binding from "./handle_select_value_binding";
import { Identifier, Node } from 'estree'; import { Identifier, Node } from "estree";
export class BaseAttributeWrapper { export class BaseAttributeWrapper {
node: Attribute; node: Attribute;
@ -46,22 +46,27 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
if (node.dependencies.size > 0) { if (node.dependencies.size > 0) {
// special case — <option value={foo}> — see below // special case — <option value={foo}> — see below
if (this.parent.node.name === 'option' && node.name === 'value') { if (this.parent.node.name === "option" && node.name === "value") {
let select: ElementWrapper = this.parent; let select: ElementWrapper = this.parent;
while (select && (select.node.type !== 'Element' || select.node.name !== 'select')) while (
select &&
(select.node.type !== "Element" || select.node.name !== "select")
)
// @ts-ignore todo: doublecheck this, but looks to be correct // @ts-ignore todo: doublecheck this, but looks to be correct
select = select.parent; select = select.parent;
if (select && select.select_binding_dependencies) { if (select && select.select_binding_dependencies) {
select.select_binding_dependencies.forEach(prop => { select.select_binding_dependencies.forEach((prop) => {
this.node.dependencies.forEach((dependency: string) => { this.node.dependencies.forEach((dependency: string) => {
this.parent.renderer.component.indirect_dependencies.get(prop).add(dependency); this.parent.renderer.component.indirect_dependencies
.get(prop)
.add(dependency);
}); });
}); });
} }
} }
if (node.name === 'value') { if (node.name === "value") {
handle_select_value_binding(this, node.dependencies); handle_select_value_binding(this, node.dependencies);
} }
} }
@ -70,28 +75,38 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
this.metadata = this.get_metadata(); this.metadata = this.get_metadata();
this.is_indirectly_bound_value = is_indirectly_bound_value(this); this.is_indirectly_bound_value = is_indirectly_bound_value(this);
this.property_name = this.is_indirectly_bound_value this.property_name = this.is_indirectly_bound_value
? '__value' ? "__value"
: this.metadata && this.metadata.property_name; : this.metadata && this.metadata.property_name;
this.is_src = this.name === 'src'; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750 this.is_src = this.name === "src"; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750
this.is_select_value_attribute = this.name === 'value' && this.parent.node.name === 'select'; this.is_select_value_attribute =
this.is_input_value = this.name === 'value' && this.parent.node.name === 'input'; this.name === "value" && this.parent.node.name === "select";
this.is_input_value =
this.name === "value" && this.parent.node.name === "input";
this.should_cache = should_cache(this); this.should_cache = should_cache(this);
} }
render(block: Block) { render(block: Block) {
const element = this.parent; const element = this.parent;
const { name, property_name, should_cache, is_indirectly_bound_value } = this; const {
name,
property_name,
should_cache,
is_indirectly_bound_value,
} = this;
// xlink is a special case... we could maybe extend this to generic // xlink is a special case... we could maybe extend this to generic
// namespaced attributes but I'm not sure that's applicable in // namespaced attributes but I'm not sure that's applicable in
// HTML5? // HTML5?
const method = /-/.test(element.node.name) const method = /-/.test(element.node.name)
? '@set_custom_element_data' ? "@set_custom_element_data"
: name.slice(0, 6) === 'xlink:' : name.slice(0, 6) === "xlink:"
? '@xlink_attr' ? "@xlink_attr"
: '@attr'; : "@attr";
const is_legacy_input_type = element.renderer.component.compile_options.legacy && name === 'type' && this.parent.node.name === 'input'; const is_legacy_input_type =
element.renderer.component.compile_options.legacy &&
name === "type" &&
this.parent.node.name === "input";
const dependencies = this.get_dependencies(); const dependencies = this.get_dependencies();
const value = this.get_value(block); const value = this.get_value(block);
@ -99,14 +114,21 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
let updater; let updater;
const init = this.get_init(block, value); const init = this.get_init(block, value);
// Set inputs value default to '' if undefined
if (name == "value") {
block.chunks.mount.push(b`@set_input_value(${element.var}, ${value});`);
}
if (is_legacy_input_type) { if (is_legacy_input_type) {
block.chunks.hydrate.push( block.chunks.hydrate.push(b`@set_input_type(${element.var}, ${init});`);
b`@set_input_type(${element.var}, ${init});` updater = b`@set_input_type(${element.var}, ${
); should_cache ? this.last : value
updater = b`@set_input_type(${element.var}, ${should_cache ? this.last : value});`; });`;
} else if (this.is_select_value_attribute) { } else if (this.is_select_value_attribute) {
// annoying special case // annoying special case
const is_multiple_select = element.node.get_static_attribute_value('multiple'); const is_multiple_select = element.node.get_static_attribute_value(
"multiple"
);
if (is_multiple_select) { if (is_multiple_select) {
updater = b`@select_options(${element.var}, ${value});`; updater = b`@select_options(${element.var}, ${value});`;
@ -121,19 +143,25 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
block.chunks.hydrate.push( block.chunks.hydrate.push(
b`if (${element.var}.src !== ${init}) ${method}(${element.var}, "${name}", ${this.last});` b`if (${element.var}.src !== ${init}) ${method}(${element.var}, "${name}", ${this.last});`
); );
updater = b`${method}(${element.var}, "${name}", ${should_cache ? this.last : value});`; updater = b`${method}(${element.var}, "${name}", ${
should_cache ? this.last : value
});`;
} else if (property_name) { } else if (property_name) {
block.chunks.hydrate.push( block.chunks.hydrate.push(b`${element.var}.${property_name} = ${init};`);
b`${element.var}.${property_name} = ${init};`
);
updater = block.renderer.options.dev updater = block.renderer.options.dev
? b`@prop_dev(${element.var}, "${property_name}", ${should_cache ? this.last : value});` ? b`@prop_dev(${element.var}, "${property_name}", ${
: b`${element.var}.${property_name} = ${should_cache ? this.last : value};`; should_cache ? this.last : value
});`
: b`${element.var}.${property_name} = ${
should_cache ? this.last : value
};`;
} else { } else {
block.chunks.hydrate.push( block.chunks.hydrate.push(
b`${method}(${element.var}, "${name}", ${init});` b`${method}(${element.var}, "${name}", ${init});`
); );
updater = b`${method}(${element.var}, "${name}", ${should_cache ? this.last : value});`; updater = b`${method}(${element.var}, "${name}", ${
should_cache ? this.last : value
});`;
} }
if (is_indirectly_bound_value) { if (is_indirectly_bound_value) {
@ -147,7 +175,10 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
} }
if (dependencies.length > 0) { if (dependencies.length > 0) {
const condition = this.get_dom_update_conditions(block, block.renderer.dirty(dependencies)); const condition = this.get_dom_update_conditions(
block,
block.renderer.dirty(dependencies)
);
block.chunks.update.push(b` block.chunks.update.push(b`
if (${condition}) { if (${condition}) {
@ -156,15 +187,20 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
} }
// special case autofocus. has to be handled in a bit of a weird way // special case autofocus. has to be handled in a bit of a weird way
if (this.node.is_true && name === 'autofocus') { if (this.node.is_true && name === "autofocus") {
block.autofocus = element.var; block.autofocus = element.var;
} }
} }
get_init(block: Block, value) { get_init(block: Block, value) {
this.last = this.should_cache && block.get_unique_name( this.last =
`${this.parent.var.name}_${this.name.replace(/[^a-zA-Z_$]/g, '_')}_value` this.should_cache &&
); block.get_unique_name(
`${this.parent.var.name}_${this.name.replace(
/[^a-zA-Z_$]/g,
"_"
)}_value`
);
if (this.should_cache) block.add_variable(this.last); if (this.should_cache) block.add_variable(this.last);
@ -185,10 +221,18 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
} }
if (this.is_input_value) { if (this.is_input_value) {
const type = element.node.get_static_attribute_value('type'); const type = element.node.get_static_attribute_value("type");
if (type === null || type === "" || type === "text" || type === "email" || type === "password") { if (
condition = x`${condition} && ${element.var}.${property_name} !== ${should_cache ? last : value}`; type === null ||
type === "" ||
type === "text" ||
type === "email" ||
type === "password"
) {
condition = x`${condition} && ${element.var}.${property_name} !== ${
should_cache ? last : value
}`;
} }
} }
@ -204,9 +248,11 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
const dependencies = new Set(node_dependencies); const dependencies = new Set(node_dependencies);
node_dependencies.forEach((prop: string) => { node_dependencies.forEach((prop: string) => {
const indirect_dependencies = this.parent.renderer.component.indirect_dependencies.get(prop); const indirect_dependencies = this.parent.renderer.component.indirect_dependencies.get(
prop
);
if (indirect_dependencies) { if (indirect_dependencies) {
indirect_dependencies.forEach(indirect_dependency => { indirect_dependencies.forEach((indirect_dependency) => {
dependencies.add(indirect_dependency); dependencies.add(indirect_dependency);
}); });
} }
@ -218,13 +264,21 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
get_metadata() { get_metadata() {
if (this.parent.node.namespace) return null; if (this.parent.node.namespace) return null;
const metadata = attribute_lookup[this.name]; const metadata = attribute_lookup[this.name];
if (metadata && metadata.applies_to && !metadata.applies_to.includes(this.parent.node.name)) return null; if (
metadata &&
metadata.applies_to &&
!metadata.applies_to.includes(this.parent.node.name)
)
return null;
return metadata; return metadata;
} }
get_value(block) { get_value(block) {
if (this.node.is_true) { if (this.node.is_true) {
if (this.metadata && boolean_attribute.has(this.metadata.property_name.toLowerCase())) { if (
this.metadata &&
boolean_attribute.has(this.metadata.property_name.toLowerCase())
) {
return x`true`; return x`true`;
} }
return x`""`; return x`""`;
@ -234,17 +288,18 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
// TODO some of this code is repeated in Tag.ts — would be good to // TODO some of this code is repeated in Tag.ts — would be good to
// DRY it out if that's possible without introducing crazy indirection // DRY it out if that's possible without introducing crazy indirection
if (this.node.chunks.length === 1) { if (this.node.chunks.length === 1) {
return this.node.chunks[0].type === 'Text' return this.node.chunks[0].type === "Text"
? string_literal((this.node.chunks[0] as Text).data) ? string_literal((this.node.chunks[0] as Text).data)
: (this.node.chunks[0] as Expression).manipulate(block); : (this.node.chunks[0] as Expression).manipulate(block);
} }
let value = this.node.name === 'class' let value =
? this.get_class_name_text(block) this.node.name === "class"
: this.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`); ? this.get_class_name_text(block)
: this.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
// '{foo} {bar}' — treat as string concatenation // '{foo} {bar}' — treat as string concatenation
if (this.node.chunks[0].type !== 'Text') { if (this.node.chunks[0].type !== "Text") {
value = x`"" + ${value}`; value = x`"" + ${value}`;
} }
@ -265,7 +320,7 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
render_chunks(block: Block) { render_chunks(block: Block) {
return this.node.chunks.map((chunk) => { return this.node.chunks.map((chunk) => {
if (chunk.type === 'Text') { if (chunk.type === "Text") {
return string_literal(chunk.data); return string_literal(chunk.data);
} }
@ -274,104 +329,114 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
} }
stringify() { stringify() {
if (this.node.is_true) return ''; if (this.node.is_true) return "";
const value = this.node.chunks; const value = this.node.chunks;
if (value.length === 0) return `=""`; if (value.length === 0) return `=""`;
return `="${value.map(chunk => { return `="${value
return chunk.type === 'Text' .map((chunk) => {
? chunk.data.replace(/"/g, '\\"') return chunk.type === "Text"
: `\${${chunk.manipulate()}}`; ? chunk.data.replace(/"/g, '\\"')
}).join('')}"`; : `\${${chunk.manipulate()}}`;
})
.join("")}"`;
} }
} }
// source: https://html.spec.whatwg.org/multipage/indices.html // source: https://html.spec.whatwg.org/multipage/indices.html
const attribute_lookup = { const attribute_lookup = {
allowfullscreen: { property_name: 'allowFullscreen', applies_to: ['iframe'] }, allowfullscreen: { property_name: "allowFullscreen", applies_to: ["iframe"] },
allowpaymentrequest: { property_name: 'allowPaymentRequest', applies_to: ['iframe'] }, allowpaymentrequest: {
async: { applies_to: ['script'] }, property_name: "allowPaymentRequest",
autofocus: { applies_to: ['button', 'input', 'keygen', 'select', 'textarea'] }, applies_to: ["iframe"],
autoplay: { applies_to: ['audio', 'video'] }, },
checked: { applies_to: ['input'] }, async: { applies_to: ["script"] },
controls: { applies_to: ['audio', 'video'] }, autofocus: {
default: { applies_to: ['track'] }, applies_to: ["button", "input", "keygen", "select", "textarea"],
defer: { applies_to: ['script'] }, },
autoplay: { applies_to: ["audio", "video"] },
checked: { applies_to: ["input"] },
controls: { applies_to: ["audio", "video"] },
default: { applies_to: ["track"] },
defer: { applies_to: ["script"] },
disabled: { disabled: {
applies_to: [ applies_to: [
'button', "button",
'fieldset', "fieldset",
'input', "input",
'keygen', "keygen",
'optgroup', "optgroup",
'option', "option",
'select', "select",
'textarea', "textarea",
], ],
}, },
formnovalidate: { property_name: 'formNoValidate', applies_to: ['button', 'input'] }, formnovalidate: {
property_name: "formNoValidate",
applies_to: ["button", "input"],
},
hidden: {}, hidden: {},
indeterminate: { applies_to: ['input'] }, indeterminate: { applies_to: ["input"] },
ismap: { property_name: 'isMap', applies_to: ['img'] }, ismap: { property_name: "isMap", applies_to: ["img"] },
loop: { applies_to: ['audio', 'bgsound', 'video'] }, loop: { applies_to: ["audio", "bgsound", "video"] },
multiple: { applies_to: ['input', 'select'] }, multiple: { applies_to: ["input", "select"] },
muted: { applies_to: ['audio', 'video'] }, muted: { applies_to: ["audio", "video"] },
nomodule: { property_name: 'noModule', applies_to: ['script'] }, nomodule: { property_name: "noModule", applies_to: ["script"] },
novalidate: { property_name: 'noValidate', applies_to: ['form'] }, novalidate: { property_name: "noValidate", applies_to: ["form"] },
open: { applies_to: ['details', 'dialog'] }, open: { applies_to: ["details", "dialog"] },
playsinline: { property_name: 'playsInline', applies_to: ['video'] }, playsinline: { property_name: "playsInline", applies_to: ["video"] },
readonly: { property_name: 'readOnly', applies_to: ['input', 'textarea'] }, readonly: { property_name: "readOnly", applies_to: ["input", "textarea"] },
required: { applies_to: ['input', 'select', 'textarea'] }, required: { applies_to: ["input", "select", "textarea"] },
reversed: { applies_to: ['ol'] }, reversed: { applies_to: ["ol"] },
selected: { applies_to: ['option'] }, selected: { applies_to: ["option"] },
value: { value: {
applies_to: [ applies_to: [
'button', "button",
'option', "option",
'input', "input",
'li', "li",
'meter', "meter",
'progress', "progress",
'param', "param",
'select', "select",
'textarea', "textarea",
], ],
}, },
}; };
Object.keys(attribute_lookup).forEach(name => { Object.keys(attribute_lookup).forEach((name) => {
const metadata = attribute_lookup[name]; const metadata = attribute_lookup[name];
if (!metadata.property_name) metadata.property_name = name; if (!metadata.property_name) metadata.property_name = name;
}); });
// source: https://html.spec.whatwg.org/multipage/indices.html // source: https://html.spec.whatwg.org/multipage/indices.html
const boolean_attribute = new Set([ const boolean_attribute = new Set([
'allowfullscreen', "allowfullscreen",
'allowpaymentrequest', "allowpaymentrequest",
'async', "async",
'autofocus', "autofocus",
'autoplay', "autoplay",
'checked', "checked",
'controls', "controls",
'default', "default",
'defer', "defer",
'disabled', "disabled",
'formnovalidate', "formnovalidate",
'hidden', "hidden",
'ismap', "ismap",
'itemscope', "itemscope",
'loop', "loop",
'multiple', "multiple",
'muted', "muted",
'nomodule', "nomodule",
'novalidate', "novalidate",
'open', "open",
'playsinline', "playsinline",
'readonly', "readonly",
'required', "required",
'reversed', "reversed",
'selected' "selected",
]); ]);
function should_cache(attribute: AttributeWrapper) { function should_cache(attribute: AttributeWrapper) {
@ -380,11 +445,12 @@ function should_cache(attribute: AttributeWrapper) {
function is_indirectly_bound_value(attribute: AttributeWrapper) { function is_indirectly_bound_value(attribute: AttributeWrapper) {
const element = attribute.parent; const element = attribute.parent;
return attribute.name === 'value' && return (
(element.node.name === 'option' || // TODO check it's actually bound attribute.name === "value" &&
(element.node.name === 'input' && (element.node.name === "option" || // TODO check it's actually bound
element.node.bindings.some( (element.node.name === "input" &&
(binding) => element.node.bindings.some((binding) =>
/checked|group/.test(binding.name) /checked|group/.test(binding.name)
))); )))
} );
}

@ -10,6 +10,7 @@ import {
noop, noop,
safe_not_equal, safe_not_equal,
set_data, set_data,
set_input_value,
space, space,
text text
} from "svelte/internal"; } from "svelte/internal";
@ -34,6 +35,7 @@ function create_fragment(ctx) {
}, },
m(target, anchor) { m(target, anchor) {
insert(target, input, anchor); insert(target, input, anchor);
set_input_value(input, /*name*/ ctx[0]);
insert(target, t0, anchor); insert(target, t0, anchor);
insert(target, h1, anchor); insert(target, h1, anchor);
append(h1, t1); append(h1, t1);

@ -8,7 +8,8 @@ import {
insert, insert,
noop, noop,
safe_not_equal, safe_not_equal,
select_option select_option,
set_input_value
} from "svelte/internal"; } from "svelte/internal";
function create_fragment(ctx) { function create_fragment(ctx) {
@ -31,7 +32,10 @@ function create_fragment(ctx) {
m(target, anchor) { m(target, anchor) {
insert(target, select, anchor); insert(target, select, anchor);
append(select, option0); append(select, option0);
set_input_value(option0, "1");
append(select, option1); append(select, option1);
set_input_value(option1, "2");
set_input_value(select, /*current*/ ctx[0]);
select_option(select, /*current*/ ctx[0]); select_option(select, /*current*/ ctx[0]);
}, },
p(ctx, [dirty]) { p(ctx, [dirty]) {

Loading…
Cancel
Save