input value default

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

@ -1,13 +1,13 @@
import Attribute from '../../../nodes/Attribute';
import Block from '../../Block';
import fix_attribute_casing from './fix_attribute_casing';
import ElementWrapper from './index';
import { string_literal } from '../../../utils/stringify';
import { b, x } from 'code-red';
import Expression from '../../../nodes/shared/Expression';
import Text from '../../../nodes/Text';
import handle_select_value_binding from './handle_select_value_binding';
import { Identifier, Node } from 'estree';
import Attribute from "../../../nodes/Attribute";
import Block from "../../Block";
import fix_attribute_casing from "./fix_attribute_casing";
import ElementWrapper from "./index";
import { string_literal } from "../../../utils/stringify";
import { b, x } from "code-red";
import Expression from "../../../nodes/shared/Expression";
import Text from "../../../nodes/Text";
import handle_select_value_binding from "./handle_select_value_binding";
import { Identifier, Node } from "estree";
export class BaseAttributeWrapper {
node: Attribute;
@ -46,22 +46,27 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
if (node.dependencies.size > 0) {
// 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;
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
select = select.parent;
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.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);
}
}
@ -70,28 +75,38 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
this.metadata = this.get_metadata();
this.is_indirectly_bound_value = is_indirectly_bound_value(this);
this.property_name = this.is_indirectly_bound_value
? '__value'
? "__value"
: 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_select_value_attribute = this.name === 'value' && this.parent.node.name === 'select';
this.is_input_value = this.name === 'value' && this.parent.node.name === 'input';
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_input_value =
this.name === "value" && this.parent.node.name === "input";
this.should_cache = should_cache(this);
}
render(block: Block) {
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
// namespaced attributes but I'm not sure that's applicable in
// HTML5?
const method = /-/.test(element.node.name)
? '@set_custom_element_data'
: name.slice(0, 6) === 'xlink:'
? '@xlink_attr'
: '@attr';
? "@set_custom_element_data"
: name.slice(0, 6) === "xlink:"
? "@xlink_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 value = this.get_value(block);
@ -99,14 +114,21 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
let updater;
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) {
block.chunks.hydrate.push(
b`@set_input_type(${element.var}, ${init});`
);
updater = b`@set_input_type(${element.var}, ${should_cache ? this.last : value});`;
block.chunks.hydrate.push(b`@set_input_type(${element.var}, ${init});`);
updater = b`@set_input_type(${element.var}, ${
should_cache ? this.last : value
});`;
} else if (this.is_select_value_attribute) {
// 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) {
updater = b`@select_options(${element.var}, ${value});`;
@ -121,19 +143,25 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
block.chunks.hydrate.push(
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) {
block.chunks.hydrate.push(
b`${element.var}.${property_name} = ${init};`
);
block.chunks.hydrate.push(b`${element.var}.${property_name} = ${init};`);
updater = block.renderer.options.dev
? b`@prop_dev(${element.var}, "${property_name}", ${should_cache ? this.last : value});`
: b`${element.var}.${property_name} = ${should_cache ? this.last : value};`;
? b`@prop_dev(${element.var}, "${property_name}", ${
should_cache ? this.last : value
});`
: b`${element.var}.${property_name} = ${
should_cache ? this.last : value
};`;
} else {
block.chunks.hydrate.push(
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) {
@ -147,7 +175,10 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
}
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`
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
if (this.node.is_true && name === 'autofocus') {
if (this.node.is_true && name === "autofocus") {
block.autofocus = element.var;
}
}
get_init(block: Block, value) {
this.last = this.should_cache && block.get_unique_name(
`${this.parent.var.name}_${this.name.replace(/[^a-zA-Z_$]/g, '_')}_value`
);
this.last =
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);
@ -185,10 +221,18 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
}
if (this.is_input_value) {
const type = element.node.get_static_attribute_value('type');
if (type === null || type === "" || type === "text" || type === "email" || type === "password") {
condition = x`${condition} && ${element.var}.${property_name} !== ${should_cache ? last : value}`;
const type = element.node.get_static_attribute_value("type");
if (
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);
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) {
indirect_dependencies.forEach(indirect_dependency => {
indirect_dependencies.forEach((indirect_dependency) => {
dependencies.add(indirect_dependency);
});
}
@ -218,13 +264,21 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
get_metadata() {
if (this.parent.node.namespace) return null;
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;
}
get_value(block) {
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`""`;
@ -234,17 +288,18 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
// 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
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)
: (this.node.chunks[0] as Expression).manipulate(block);
}
let value = this.node.name === 'class'
? this.get_class_name_text(block)
: this.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
let value =
this.node.name === "class"
? this.get_class_name_text(block)
: this.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
// '{foo} {bar}' — treat as string concatenation
if (this.node.chunks[0].type !== 'Text') {
if (this.node.chunks[0].type !== "Text") {
value = x`"" + ${value}`;
}
@ -265,7 +320,7 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
render_chunks(block: Block) {
return this.node.chunks.map((chunk) => {
if (chunk.type === 'Text') {
if (chunk.type === "Text") {
return string_literal(chunk.data);
}
@ -274,104 +329,114 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
}
stringify() {
if (this.node.is_true) return '';
if (this.node.is_true) return "";
const value = this.node.chunks;
if (value.length === 0) return `=""`;
return `="${value.map(chunk => {
return chunk.type === 'Text'
? chunk.data.replace(/"/g, '\\"')
: `\${${chunk.manipulate()}}`;
}).join('')}"`;
return `="${value
.map((chunk) => {
return chunk.type === "Text"
? chunk.data.replace(/"/g, '\\"')
: `\${${chunk.manipulate()}}`;
})
.join("")}"`;
}
}
// source: https://html.spec.whatwg.org/multipage/indices.html
const attribute_lookup = {
allowfullscreen: { property_name: 'allowFullscreen', applies_to: ['iframe'] },
allowpaymentrequest: { property_name: 'allowPaymentRequest', applies_to: ['iframe'] },
async: { applies_to: ['script'] },
autofocus: { applies_to: ['button', 'input', 'keygen', 'select', 'textarea'] },
autoplay: { applies_to: ['audio', 'video'] },
checked: { applies_to: ['input'] },
controls: { applies_to: ['audio', 'video'] },
default: { applies_to: ['track'] },
defer: { applies_to: ['script'] },
allowfullscreen: { property_name: "allowFullscreen", applies_to: ["iframe"] },
allowpaymentrequest: {
property_name: "allowPaymentRequest",
applies_to: ["iframe"],
},
async: { applies_to: ["script"] },
autofocus: {
applies_to: ["button", "input", "keygen", "select", "textarea"],
},
autoplay: { applies_to: ["audio", "video"] },
checked: { applies_to: ["input"] },
controls: { applies_to: ["audio", "video"] },
default: { applies_to: ["track"] },
defer: { applies_to: ["script"] },
disabled: {
applies_to: [
'button',
'fieldset',
'input',
'keygen',
'optgroup',
'option',
'select',
'textarea',
"button",
"fieldset",
"input",
"keygen",
"optgroup",
"option",
"select",
"textarea",
],
},
formnovalidate: { property_name: 'formNoValidate', applies_to: ['button', 'input'] },
formnovalidate: {
property_name: "formNoValidate",
applies_to: ["button", "input"],
},
hidden: {},
indeterminate: { applies_to: ['input'] },
ismap: { property_name: 'isMap', applies_to: ['img'] },
loop: { applies_to: ['audio', 'bgsound', 'video'] },
multiple: { applies_to: ['input', 'select'] },
muted: { applies_to: ['audio', 'video'] },
nomodule: { property_name: 'noModule', applies_to: ['script'] },
novalidate: { property_name: 'noValidate', applies_to: ['form'] },
open: { applies_to: ['details', 'dialog'] },
playsinline: { property_name: 'playsInline', applies_to: ['video'] },
readonly: { property_name: 'readOnly', applies_to: ['input', 'textarea'] },
required: { applies_to: ['input', 'select', 'textarea'] },
reversed: { applies_to: ['ol'] },
selected: { applies_to: ['option'] },
indeterminate: { applies_to: ["input"] },
ismap: { property_name: "isMap", applies_to: ["img"] },
loop: { applies_to: ["audio", "bgsound", "video"] },
multiple: { applies_to: ["input", "select"] },
muted: { applies_to: ["audio", "video"] },
nomodule: { property_name: "noModule", applies_to: ["script"] },
novalidate: { property_name: "noValidate", applies_to: ["form"] },
open: { applies_to: ["details", "dialog"] },
playsinline: { property_name: "playsInline", applies_to: ["video"] },
readonly: { property_name: "readOnly", applies_to: ["input", "textarea"] },
required: { applies_to: ["input", "select", "textarea"] },
reversed: { applies_to: ["ol"] },
selected: { applies_to: ["option"] },
value: {
applies_to: [
'button',
'option',
'input',
'li',
'meter',
'progress',
'param',
'select',
'textarea',
"button",
"option",
"input",
"li",
"meter",
"progress",
"param",
"select",
"textarea",
],
},
};
Object.keys(attribute_lookup).forEach(name => {
Object.keys(attribute_lookup).forEach((name) => {
const metadata = attribute_lookup[name];
if (!metadata.property_name) metadata.property_name = name;
});
// source: https://html.spec.whatwg.org/multipage/indices.html
const boolean_attribute = new Set([
'allowfullscreen',
'allowpaymentrequest',
'async',
'autofocus',
'autoplay',
'checked',
'controls',
'default',
'defer',
'disabled',
'formnovalidate',
'hidden',
'ismap',
'itemscope',
'loop',
'multiple',
'muted',
'nomodule',
'novalidate',
'open',
'playsinline',
'readonly',
'required',
'reversed',
'selected'
"allowfullscreen",
"allowpaymentrequest",
"async",
"autofocus",
"autoplay",
"checked",
"controls",
"default",
"defer",
"disabled",
"formnovalidate",
"hidden",
"ismap",
"itemscope",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"readonly",
"required",
"reversed",
"selected",
]);
function should_cache(attribute: AttributeWrapper) {
@ -380,11 +445,12 @@ function should_cache(attribute: AttributeWrapper) {
function is_indirectly_bound_value(attribute: AttributeWrapper) {
const element = attribute.parent;
return attribute.name === 'value' &&
(element.node.name === 'option' || // TODO check it's actually bound
(element.node.name === 'input' &&
element.node.bindings.some(
(binding) =>
/checked|group/.test(binding.name)
)));
return (
attribute.name === "value" &&
(element.node.name === "option" || // TODO check it's actually bound
(element.node.name === "input" &&
element.node.bindings.some((binding) =>
/checked|group/.test(binding.name)
)))
);
}

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

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

Loading…
Cancel
Save