mirror of https://github.com/sveltejs/svelte
commit
64d53563c4
@ -0,0 +1,41 @@
|
|||||||
|
import Renderer from '../Renderer';
|
||||||
|
import Block from '../Block';
|
||||||
|
import Comment from '../../nodes/Comment';
|
||||||
|
import Wrapper from './shared/Wrapper';
|
||||||
|
import { x } from 'code-red';
|
||||||
|
import { Identifier } from 'estree';
|
||||||
|
|
||||||
|
export default class CommentWrapper extends Wrapper {
|
||||||
|
node: Comment;
|
||||||
|
var: Identifier;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
renderer: Renderer,
|
||||||
|
block: Block,
|
||||||
|
parent: Wrapper,
|
||||||
|
node: Comment
|
||||||
|
) {
|
||||||
|
super(renderer, block, parent, node);
|
||||||
|
this.var = x`c` as Identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
|
||||||
|
if (!this.renderer.options.preserveComments) return;
|
||||||
|
|
||||||
|
const string_literal = {
|
||||||
|
type: 'Literal',
|
||||||
|
value: this.node.data,
|
||||||
|
loc: {
|
||||||
|
start: this.renderer.locate(this.node.start),
|
||||||
|
end: this.renderer.locate(this.node.end)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
block.add_element(
|
||||||
|
this.var,
|
||||||
|
x`@comment(${string_literal})`,
|
||||||
|
parent_nodes && x`@claim_comment(${parent_nodes}, ${string_literal})`,
|
||||||
|
parent_node
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
// Note: Must import from the `css-tree` browser bundled distribution due to `createRequire` usage if importing from
|
||||||
|
// `css-tree` Node module directly. This allows the production build of Svelte to work correctly.
|
||||||
|
import { fork } from '../../../../../node_modules/css-tree/dist/csstree.esm.js';
|
||||||
|
|
||||||
|
import * as node from './node';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends `css-tree` for container query support by forking and adding new nodes and at-rule support for `@container`.
|
||||||
|
*
|
||||||
|
* The new nodes are located in `./node`.
|
||||||
|
*/
|
||||||
|
const cqSyntax = fork({
|
||||||
|
atrule: { // extend or override at-rule dictionary
|
||||||
|
container: {
|
||||||
|
parse: {
|
||||||
|
prelude() {
|
||||||
|
return this.createSingleNodeList(
|
||||||
|
this.ContainerQuery()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
block(isStyleBlock = false) {
|
||||||
|
return this.Block(isStyleBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
node
|
||||||
|
});
|
||||||
|
|
||||||
|
export const parse = cqSyntax.parse;
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { Delim } from 'css-tree/tokenizer';
|
||||||
|
|
||||||
|
export const name = 'Comparison';
|
||||||
|
export const structure = {
|
||||||
|
value: String
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parse() {
|
||||||
|
const start = this.tokenStart;
|
||||||
|
|
||||||
|
const char1 = this.consume(Delim);
|
||||||
|
|
||||||
|
// The first character in the comparison operator must match '<', '=', or '>'.
|
||||||
|
if (char1 !== '<' && char1 !== '>' && char1 !== '=') {
|
||||||
|
this.error('Malformed comparison operator');
|
||||||
|
}
|
||||||
|
|
||||||
|
let char2;
|
||||||
|
|
||||||
|
if (this.tokenType === Delim) {
|
||||||
|
char2 = this.consume(Delim);
|
||||||
|
|
||||||
|
// The second character in the comparison operator must match '='.
|
||||||
|
if (char2 !== '=') {
|
||||||
|
this.error('Malformed comparison operator');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the next token is also 'Delim' then it is malformed.
|
||||||
|
if (this.tokenType === Delim) {
|
||||||
|
this.error('Malformed comparison operator');
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = char2 ? `${char1}${char2}` : char1;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'Comparison',
|
||||||
|
loc: this.getLocation(start, this.tokenStart),
|
||||||
|
value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate(node) {
|
||||||
|
for (let index = 0; index < node.value.length; index++) {
|
||||||
|
this.token(Delim, node.value.charAt(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import {
|
||||||
|
Function,
|
||||||
|
Ident,
|
||||||
|
Number,
|
||||||
|
Dimension,
|
||||||
|
RightParenthesis,
|
||||||
|
Colon,
|
||||||
|
Delim
|
||||||
|
} from 'css-tree/tokenizer';
|
||||||
|
|
||||||
|
export const name = 'ContainerFeatureStyle';
|
||||||
|
export const structure = {
|
||||||
|
name: String,
|
||||||
|
value: ['Function', 'Identifier', 'Number', 'Dimension', 'QueryCSSFunction', 'Ratio', null]
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parse() {
|
||||||
|
const start = this.tokenStart;
|
||||||
|
let value = null;
|
||||||
|
|
||||||
|
const function_name = this.consumeFunctionName();
|
||||||
|
if (function_name !== 'style') {
|
||||||
|
this.error('Unknown container style query identifier; "style" is expected');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
const name = this.consume(Ident);
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
if (this.tokenType !== RightParenthesis) {
|
||||||
|
this.eat(Colon);
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
switch (this.tokenType) {
|
||||||
|
case Number:
|
||||||
|
if (this.lookupNonWSType(1) === Delim) {
|
||||||
|
value = this.Ratio();
|
||||||
|
} else {
|
||||||
|
value = this.Number();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Dimension:
|
||||||
|
value = this.Dimension();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Function:
|
||||||
|
value = this.QueryCSSFunction();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Ident:
|
||||||
|
value = this.Identifier();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.error('Number, dimension, ratio, function or identifier is expected');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.skipSC();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eat(RightParenthesis);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'ContainerFeatureStyle',
|
||||||
|
loc: this.getLocation(start, this.tokenStart),
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate(node) {
|
||||||
|
this.token(Function, 'style(');
|
||||||
|
this.token(Ident, node.name);
|
||||||
|
|
||||||
|
if (node.value !== null) {
|
||||||
|
this.token(Colon, ':');
|
||||||
|
this.node(node.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.token(RightParenthesis, ')');
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import {
|
||||||
|
WhiteSpace,
|
||||||
|
Comment,
|
||||||
|
Function,
|
||||||
|
Ident,
|
||||||
|
LeftParenthesis
|
||||||
|
} from 'css-tree/tokenizer';
|
||||||
|
|
||||||
|
import { lookahead_is_range } from './lookahead_is_range';
|
||||||
|
|
||||||
|
const CONTAINER_QUERY_KEYWORDS = new Set(['none', 'and', 'not', 'or']);
|
||||||
|
|
||||||
|
export const name = 'ContainerQuery';
|
||||||
|
export const structure = {
|
||||||
|
name: 'Identifier',
|
||||||
|
children: [[
|
||||||
|
'Identifier',
|
||||||
|
'QueryFeature',
|
||||||
|
'QueryFeatureRange',
|
||||||
|
'ContainerFeatureStyle',
|
||||||
|
'WhiteSpace'
|
||||||
|
]]
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parse() {
|
||||||
|
const start = this.tokenStart;
|
||||||
|
const children = this.createList();
|
||||||
|
let child = null;
|
||||||
|
let name = null;
|
||||||
|
|
||||||
|
// Parse potential container name.
|
||||||
|
if (this.tokenType === Ident) {
|
||||||
|
const container_name = this.substring(this.tokenStart, this.tokenEnd);
|
||||||
|
|
||||||
|
// Container name doesn't match a query keyword, so assign it as container name.
|
||||||
|
if (!CONTAINER_QUERY_KEYWORDS.has(container_name.toLowerCase())) {
|
||||||
|
name = container_name;
|
||||||
|
this.eatIdent(container_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
scan:
|
||||||
|
while (!this.eof) {
|
||||||
|
switch (this.tokenType) {
|
||||||
|
case Comment:
|
||||||
|
case WhiteSpace:
|
||||||
|
this.next();
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case Ident:
|
||||||
|
child = this.Identifier();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Function:
|
||||||
|
child = this.ContainerFeatureStyle();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LeftParenthesis:
|
||||||
|
// Lookahead to determine if range feature.
|
||||||
|
child = lookahead_is_range.call(this) ? this.QueryFeatureRange() : this.QueryFeature();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
children.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child === null) {
|
||||||
|
this.error('Identifier or parenthesis is expected');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'ContainerQuery',
|
||||||
|
loc: this.getLocation(start, this.tokenStart - 1),
|
||||||
|
name,
|
||||||
|
children
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate(node) {
|
||||||
|
if (typeof node.name === 'string') {
|
||||||
|
this.token(Ident, node.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.children(node);
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
export * as Comparison from './comparison';
|
||||||
|
export * as ContainerFeatureStyle from './container_feature_style';
|
||||||
|
export * as ContainerQuery from './container_query';
|
||||||
|
export * as MediaQuery from './media_query';
|
||||||
|
export * as QueryFeature from './query_feature';
|
||||||
|
export * as QueryFeatureRange from './query_feature_range';
|
||||||
|
export * as QueryCSSFunction from './query_css_function';
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import {
|
||||||
|
EOF,
|
||||||
|
WhiteSpace,
|
||||||
|
Delim,
|
||||||
|
RightParenthesis,
|
||||||
|
LeftCurlyBracket,
|
||||||
|
Colon
|
||||||
|
} from 'css-tree/tokenizer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks ahead to determine if query feature is a range query. This involves locating at least one delimiter and no
|
||||||
|
* colon tokens.
|
||||||
|
*
|
||||||
|
* @returns {boolean} Is potential range query.
|
||||||
|
*/
|
||||||
|
export function lookahead_is_range() {
|
||||||
|
let type;
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
let delim_found = false;
|
||||||
|
let no_colon = true;
|
||||||
|
|
||||||
|
// A range query has maximum 5 tokens when formatted as 'mf-range' /
|
||||||
|
// '<mf-value> <mf-lt> <mf-name> <mf-lt> <mf-value>'. So only look ahead maximum of 6 non-whitespace tokens.
|
||||||
|
do {
|
||||||
|
type = this.lookupNonWSType(offset++);
|
||||||
|
if (type !== WhiteSpace) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (type === Delim) {
|
||||||
|
delim_found = true;
|
||||||
|
}
|
||||||
|
if (type === Colon) {
|
||||||
|
no_colon = false;
|
||||||
|
}
|
||||||
|
if (type === LeftCurlyBracket || type === RightParenthesis) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (type !== EOF && count <= 6);
|
||||||
|
|
||||||
|
return delim_found && no_colon;
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import {
|
||||||
|
WhiteSpace,
|
||||||
|
Comment,
|
||||||
|
Ident,
|
||||||
|
LeftParenthesis
|
||||||
|
} from 'css-tree/tokenizer';
|
||||||
|
|
||||||
|
import { lookahead_is_range } from './lookahead_is_range';
|
||||||
|
|
||||||
|
export const name = 'MediaQuery';
|
||||||
|
export const structure = {
|
||||||
|
children: [[
|
||||||
|
'Identifier',
|
||||||
|
'QueryFeature',
|
||||||
|
'QueryFeatureRange',
|
||||||
|
'WhiteSpace'
|
||||||
|
]]
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parse() {
|
||||||
|
const children = this.createList();
|
||||||
|
let child = null;
|
||||||
|
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
scan:
|
||||||
|
while (!this.eof) {
|
||||||
|
switch (this.tokenType) {
|
||||||
|
case Comment:
|
||||||
|
case WhiteSpace:
|
||||||
|
this.next();
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case Ident:
|
||||||
|
child = this.Identifier();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LeftParenthesis:
|
||||||
|
// Lookahead to determine if range feature.
|
||||||
|
child = lookahead_is_range.call(this) ? this.QueryFeatureRange() : this.QueryFeature();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
children.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child === null) {
|
||||||
|
this.error('Identifier or parenthesis is expected');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'MediaQuery',
|
||||||
|
loc: this.getLocationFromList(children),
|
||||||
|
children
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate(node) {
|
||||||
|
this.children(node);
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import {
|
||||||
|
RightParenthesis
|
||||||
|
} from 'css-tree/tokenizer';
|
||||||
|
|
||||||
|
const QUERY_CSS_FUNCTIONS = new Set(['calc', 'clamp', 'min', 'max']);
|
||||||
|
|
||||||
|
export const name = 'QueryCSSFunction';
|
||||||
|
export const structure = {
|
||||||
|
name: String,
|
||||||
|
expression: String
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parse() {
|
||||||
|
const start = this.tokenStart;
|
||||||
|
|
||||||
|
const name = this.consumeFunctionName();
|
||||||
|
|
||||||
|
if (!QUERY_CSS_FUNCTIONS.has(name)) {
|
||||||
|
this.error('Unknown query single value function; expected: "calc", "clamp", "max", min"');
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = this.Raw(this.tokenIndex, null, false);
|
||||||
|
|
||||||
|
this.eat(RightParenthesis);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'QueryCSSFunction',
|
||||||
|
loc: this.getLocation(start, this.tokenStart),
|
||||||
|
name,
|
||||||
|
expression: body.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate(node) {
|
||||||
|
this.token(Function, `${node.name}(`);
|
||||||
|
|
||||||
|
this.node(node.expression);
|
||||||
|
|
||||||
|
this.token(RightParenthesis, ')');
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import {
|
||||||
|
Ident,
|
||||||
|
Number,
|
||||||
|
Dimension,
|
||||||
|
Function,
|
||||||
|
LeftParenthesis,
|
||||||
|
RightParenthesis,
|
||||||
|
Colon,
|
||||||
|
Delim
|
||||||
|
} from 'css-tree/tokenizer';
|
||||||
|
|
||||||
|
export const name = 'QueryFeature';
|
||||||
|
export const structure = {
|
||||||
|
name: String,
|
||||||
|
value: ['Identifier', 'Number', 'Dimension', 'QueryCSSFunction', 'Ratio', null]
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parse() {
|
||||||
|
const start = this.tokenStart;
|
||||||
|
let value = null;
|
||||||
|
|
||||||
|
this.eat(LeftParenthesis);
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
const name = this.consume(Ident);
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
if (this.tokenType !== RightParenthesis) {
|
||||||
|
this.eat(Colon);
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
switch (this.tokenType) {
|
||||||
|
case Number:
|
||||||
|
if (this.lookupNonWSType(1) === Delim) {
|
||||||
|
value = this.Ratio();
|
||||||
|
} else {
|
||||||
|
value = this.Number();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Dimension:
|
||||||
|
value = this.Dimension();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Function:
|
||||||
|
value = this.QueryCSSFunction();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Ident:
|
||||||
|
value = this.Identifier();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.error('Number, dimension, ratio, function, or identifier is expected');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.skipSC();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eat(RightParenthesis);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'QueryFeature',
|
||||||
|
loc: this.getLocation(start, this.tokenStart),
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate(node) {
|
||||||
|
this.token(LeftParenthesis, '(');
|
||||||
|
this.token(Ident, node.name);
|
||||||
|
|
||||||
|
if (node.value !== null) {
|
||||||
|
this.token(Colon, ':');
|
||||||
|
this.node(node.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.token(RightParenthesis, ')');
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import {
|
||||||
|
Ident,
|
||||||
|
Number,
|
||||||
|
Delim,
|
||||||
|
Dimension,
|
||||||
|
Function,
|
||||||
|
LeftParenthesis,
|
||||||
|
RightParenthesis,
|
||||||
|
WhiteSpace
|
||||||
|
} from 'css-tree/tokenizer';
|
||||||
|
|
||||||
|
export const name = 'QueryFeatureRange';
|
||||||
|
export const structure = {
|
||||||
|
name: String,
|
||||||
|
value: ['Identifier', 'Number', 'Comparison', 'Dimension', 'QueryCSSFunction', 'Ratio', null]
|
||||||
|
};
|
||||||
|
|
||||||
|
function lookup_non_WS_type_and_value(offset, type, referenceStr) {
|
||||||
|
let current_type;
|
||||||
|
|
||||||
|
do {
|
||||||
|
current_type = this.lookupType(offset++);
|
||||||
|
if (current_type !== WhiteSpace) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (current_type !== 0); // NULL -> 0
|
||||||
|
|
||||||
|
return current_type === type ? this.lookupValue(offset - 1, referenceStr) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse() {
|
||||||
|
const start = this.tokenStart;
|
||||||
|
const children = this.createList();
|
||||||
|
let child = null;
|
||||||
|
|
||||||
|
this.eat(LeftParenthesis);
|
||||||
|
this.skipSC();
|
||||||
|
|
||||||
|
while (!this.eof && this.tokenType !== RightParenthesis) {
|
||||||
|
switch (this.tokenType) {
|
||||||
|
case Number:
|
||||||
|
if (lookup_non_WS_type_and_value.call(this, 1, Delim, '/')) {
|
||||||
|
child = this.Ratio();
|
||||||
|
} else {
|
||||||
|
child = this.Number();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Delim:
|
||||||
|
child = this.Comparison();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Dimension:
|
||||||
|
child = this.Dimension();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Function:
|
||||||
|
child = this.QueryCSSFunction();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Ident:
|
||||||
|
child = this.Identifier();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.error('Number, dimension, comparison, ratio, function, or identifier is expected');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
children.push(child);
|
||||||
|
|
||||||
|
this.skipSC();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eat(RightParenthesis);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'QueryFeatureRange',
|
||||||
|
loc: this.getLocation(start, this.tokenStart),
|
||||||
|
children
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate(node) {
|
||||||
|
this.children(node);
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* Resize observer singleton.
|
||||||
|
* One listener per element only!
|
||||||
|
* https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A/m/F5-VcUZtBAAJ
|
||||||
|
*/
|
||||||
|
export class ResizeObserverSingleton {
|
||||||
|
constructor(readonly options?: ResizeObserverOptions) {}
|
||||||
|
|
||||||
|
observe(element: Element, listener: Listener) {
|
||||||
|
this._listeners.set(element, listener);
|
||||||
|
this._getObserver().observe(element, this.options);
|
||||||
|
return () => {
|
||||||
|
this._listeners.delete(element);
|
||||||
|
this._observer.unobserve(element); // this line can probably be removed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly entries: WeakMap<Element, ResizeObserverEntry> = 'WeakMap' in globalThis ? new WeakMap() : undefined;
|
||||||
|
|
||||||
|
private readonly _listeners: WeakMap<Element, Listener> = 'WeakMap' in globalThis ? new WeakMap() : undefined;
|
||||||
|
private _observer?: ResizeObserver;
|
||||||
|
private _getObserver() {
|
||||||
|
return this._observer ?? (this._observer = new ResizeObserver((entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
ResizeObserverSingleton.entries.set(entry.target, entry);
|
||||||
|
this._listeners.get(entry.target)?.(entry);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Listener = (entry: ResizeObserverEntry)=>any;
|
||||||
|
|
||||||
|
// TODO: Remove this
|
||||||
|
interface ResizeObserverSize {
|
||||||
|
readonly blockSize: number;
|
||||||
|
readonly inlineSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResizeObserverEntry {
|
||||||
|
readonly borderBoxSize: readonly ResizeObserverSize[];
|
||||||
|
readonly contentBoxSize: readonly ResizeObserverSize[];
|
||||||
|
readonly contentRect: DOMRectReadOnly;
|
||||||
|
readonly devicePixelContentBoxSize: readonly ResizeObserverSize[];
|
||||||
|
readonly target: Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResizeObserverBoxOptions = 'border-box' | 'content-box' | 'device-pixel-content-box';
|
||||||
|
|
||||||
|
interface ResizeObserverOptions {
|
||||||
|
box?: ResizeObserverBoxOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResizeObserver {
|
||||||
|
disconnect(): void;
|
||||||
|
observe(target: Element, options?: ResizeObserverOptions): void;
|
||||||
|
unobserve(target: Element): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResizeObserverCallback {
|
||||||
|
(entries: ResizeObserverEntry[], observer: ResizeObserver): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare let ResizeObserver: {
|
||||||
|
prototype: ResizeObserver;
|
||||||
|
new(callback: ResizeObserverCallback): ResizeObserver;
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
div.svelte-xyz{container:test-container / inline-size}@container (min-width: 400px){div.svelte-xyz{color:red}}@container test-container (min-width: 410px){div.svelte-xyz{color:green}}@container test-container (width < 400px){div.svelte-xyz{color:blue}}@container test-container (0 <= width < 300px){div.svelte-xyz{color:purple}}@container not (width < 400px){div.svelte-xyz{color:pink}}@container (width > 400px) and (height > 400px){div.svelte-xyz{color:lightgreen}}@container (width > 400px) or (height > 400px){div.svelte-xyz{color:lightblue}}@container (width > 400px) and (width > 800px) or (orientation: portrait){div.svelte-xyz{color:salmon}}@container style(color: blue){div.svelte-xyz{color:tan}}@container test-container (min-width: calc(400px + 1px)){div.svelte-xyz{color:green}}@container test-container (width < clamp(200px, 40%, 400px)){div.svelte-xyz{color:blue}}@container test-container (calc(400px + 1px) <= width < calc(500px + 1px)){div.svelte-xyz{color:purple}}@container style(--var: calc(400px + 1px)){div.svelte-xyz{color:sandybrown}}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
<div>container query</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
container: test-container / inline-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Most common container query statements. */
|
||||||
|
|
||||||
|
@container (min-width: 400px) {
|
||||||
|
div {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container test-container (min-width: 410px) {
|
||||||
|
div {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container test-container (width < 400px) {
|
||||||
|
div {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container test-container (0 <= width < 300px) {
|
||||||
|
div {
|
||||||
|
color: purple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container not (width < 400px) {
|
||||||
|
div {
|
||||||
|
color: pink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (width > 400px) and (height > 400px) {
|
||||||
|
div {
|
||||||
|
color: lightgreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (width > 400px) or (height > 400px) {
|
||||||
|
div {
|
||||||
|
color: lightblue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (width > 400px) and (width > 800px) or (orientation: portrait) {
|
||||||
|
div {
|
||||||
|
color: salmon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container style(color: blue) {
|
||||||
|
div {
|
||||||
|
color: tan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container test-container (min-width: calc(400px + 1px)) {
|
||||||
|
div {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container test-container (width < clamp(200px, 40%, 400px)) {
|
||||||
|
div {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container test-container (calc(400px + 1px) <= width < calc(500px + 1px)) {
|
||||||
|
div {
|
||||||
|
color: purple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container style(--var: calc(400px + 1px)) {
|
||||||
|
div {
|
||||||
|
color: sandybrown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1 +1 @@
|
|||||||
@media(min-width: 400px){.large-screen.svelte-xyz{display:block}}
|
@media(min-width: 400px){.large-screen.svelte-xyz{display:block}}@media(min-width: calc(400px + 1px)){.large-screen.svelte-xyz{display:block}}@media(width >= 600px){.large-screen.svelte-xyz{display:block}}@media(400px <= width <= 1000px){.large-screen.svelte-xyz{display:block}}@media(width < clamp(200px, 40%, 400px)){.large-screen.svelte-xyz{display:block}}@media(calc(400px + 1px) <= width <= calc(1000px + 1px)){.large-screen.svelte-xyz{display:block}}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<div><!-- test1 --><!-- test2 --></div>
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<div><!-- test1 --></div>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
export default {
|
||||||
|
compileOptions: {
|
||||||
|
preserveComments:true
|
||||||
|
},
|
||||||
|
snapshot(target) {
|
||||||
|
const div = target.querySelector('div');
|
||||||
|
|
||||||
|
return {
|
||||||
|
div,
|
||||||
|
comment: div.childNodes[0]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert, target, snapshot) {
|
||||||
|
const div = target.querySelector('div');
|
||||||
|
assert.equal(div, snapshot.div);
|
||||||
|
assert.equal(div.childNodes[0], snapshot.comment);
|
||||||
|
assert.equal(div.childNodes[1].nodeType, 8);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<div><!-- test1 --><!-- test2 --></div>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export default {
|
||||||
|
options: {
|
||||||
|
preserveComments: true
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
/* generated by Svelte vX.Y.Z */
|
||||||
|
import {
|
||||||
|
SvelteComponent,
|
||||||
|
comment,
|
||||||
|
detach,
|
||||||
|
element,
|
||||||
|
init,
|
||||||
|
insert,
|
||||||
|
noop,
|
||||||
|
safe_not_equal,
|
||||||
|
space
|
||||||
|
} from "svelte/internal";
|
||||||
|
|
||||||
|
function create_fragment(ctx) {
|
||||||
|
let div0;
|
||||||
|
let t1;
|
||||||
|
let c;
|
||||||
|
let t2;
|
||||||
|
let div1;
|
||||||
|
|
||||||
|
return {
|
||||||
|
c() {
|
||||||
|
div0 = element("div");
|
||||||
|
div0.textContent = "content";
|
||||||
|
t1 = space();
|
||||||
|
c = comment(" comment ");
|
||||||
|
t2 = space();
|
||||||
|
div1 = element("div");
|
||||||
|
div1.textContent = "more content";
|
||||||
|
},
|
||||||
|
m(target, anchor) {
|
||||||
|
insert(target, div0, anchor);
|
||||||
|
insert(target, t1, anchor);
|
||||||
|
insert(target, c, anchor);
|
||||||
|
insert(target, t2, anchor);
|
||||||
|
insert(target, div1, anchor);
|
||||||
|
},
|
||||||
|
p: noop,
|
||||||
|
i: noop,
|
||||||
|
o: noop,
|
||||||
|
d(detaching) {
|
||||||
|
if (detaching) detach(div0);
|
||||||
|
if (detaching) detach(t1);
|
||||||
|
if (detaching) detach(c);
|
||||||
|
if (detaching) detach(t2);
|
||||||
|
if (detaching) detach(div1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Component extends SvelteComponent {
|
||||||
|
constructor(options) {
|
||||||
|
super();
|
||||||
|
init(this, options, null, create_fragment, safe_not_equal, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Component;
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<div>content</div>
|
||||||
|
<!-- comment -->
|
||||||
|
<div>more content</div>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
// A puppeteer test because JSDOM doesn't support contenteditable
|
||||||
|
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>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
// A puppeteer test because JSDOM doesn't support contenteditable
|
||||||
|
export default {
|
||||||
|
html: '<div contenteditable="true"></div>',
|
||||||
|
ssrHtml: '<div contenteditable=""></div>',
|
||||||
|
|
||||||
|
async test({ assert, target, window }) {
|
||||||
|
// this tests that by going from contenteditable=true to false, the
|
||||||
|
// content is correctly updated before that. This relies on the order
|
||||||
|
// of the updates: first updating the content, then setting contenteditable
|
||||||
|
// to false, which means that `set_data_maybe_contenteditable` is used and not `set_data`.
|
||||||
|
// If the order is reversed, https://github.com/sveltejs/svelte/issues/5018
|
||||||
|
// would be happening. The caveat is that if we go from contenteditable=false to true
|
||||||
|
// then we will have the same issue. To fix this reliably we probably need to
|
||||||
|
// overhaul the way we handle text updates in general.
|
||||||
|
// If due to some refactoring this test fails, it's probably fine to ignore it since
|
||||||
|
// this is a very specific edge case and the behavior is unstable anyway.
|
||||||
|
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,11 @@
|
|||||||
|
<script>
|
||||||
|
let text = "";
|
||||||
|
const updater = (event) => {
|
||||||
|
text = event.target.textContent;
|
||||||
|
};
|
||||||
|
$: spread = {
|
||||||
|
contenteditable: text !== "a",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div {...spread} on:input={updater}>{text}</div>
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
// A puppeteer test because JSDOM doesn't support contenteditable
|
||||||
|
export default {
|
||||||
|
html: '<div contenteditable=""></div>',
|
||||||
|
|
||||||
|
// Failing test for https://github.com/sveltejs/svelte/issues/5018, fix pending
|
||||||
|
// It's hard to fix this because in order to do that, we would need to change the
|
||||||
|
// way the value is compared completely. Right now it compares the value of the
|
||||||
|
// first text node, but it should compare the value of the whole content
|
||||||
|
skip: true,
|
||||||
|
|
||||||
|
async test({ assert, target, window }) {
|
||||||
|
const div = target.querySelector('div');
|
||||||
|
|
||||||
|
let text = window.document.createTextNode('a');
|
||||||
|
div.insertBefore(text, null);
|
||||||
|
let event = new window.InputEvent('input');
|
||||||
|
await div.dispatchEvent(event);
|
||||||
|
assert.equal(div.textContent, 'a');
|
||||||
|
|
||||||
|
// When a user types a newline, the browser inserts a <div> element
|
||||||
|
const inner_div = window.document.createElement('div');
|
||||||
|
div.insertBefore(inner_div, null);
|
||||||
|
event = new window.InputEvent('input');
|
||||||
|
await div.dispatchEvent(event);
|
||||||
|
assert.equal(div.textContent, 'a');
|
||||||
|
|
||||||
|
text = window.document.createTextNode('b');
|
||||||
|
inner_div.insertBefore(text, null);
|
||||||
|
event = new window.InputEvent('input');
|
||||||
|
await div.dispatchEvent(event);
|
||||||
|
assert.equal(div.textContent, 'ab');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
html: '<div hidden />',
|
||||||
|
test({ assert, component, target }) {
|
||||||
|
component.hidden = false;
|
||||||
|
assert.htmlEqual(target.innerHTML, '<div />');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<script>
|
||||||
|
export let hidden = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div {hidden} />
|
||||||
@ -1,11 +0,0 @@
|
|||||||
export default {
|
|
||||||
props: {
|
|
||||||
itemscope: true
|
|
||||||
},
|
|
||||||
test({ assert, target, component }) {
|
|
||||||
const div = target.querySelector('div');
|
|
||||||
assert.ok(div.itemscope);
|
|
||||||
component.itemscope = false;
|
|
||||||
assert.ok(!div.itemscope);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
<script>
|
|
||||||
export let itemscope;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div {itemscope} />
|
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
export default {
|
||||||
|
html: '<textarea></textarea>',
|
||||||
|
ssrHtml: '<textarea>test\'"></textarea><script>alert(\'BIM\');</script></textarea>'
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<textarea value={`test'"></textarea><script>alert('BIM');</script>`} />
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
// There is no relationship between the attribute and the dom node with regards to microdata attributes https://developer.mozilla.org/en-US/docs/Web/HTML/Microdata
|
||||||
|
export default {
|
||||||
|
html: `<div itemscope itemtype="https://schema.org/SoftwareApplication">
|
||||||
|
<span itemprop="name">Game</span> - REQUIRES
|
||||||
|
<span itemprop="operatingSystem">OS</span><br/>
|
||||||
|
<link itemprop="applicationCategory" href="https://schema.org/GameApplication"/>
|
||||||
|
|
||||||
|
<div itemprop="aggregateRating" itemscope="" itemtype="https://schema.org/AggregateRating">RATING:
|
||||||
|
<span itemprop="ratingValue">4.6</span> (
|
||||||
|
<span itemprop="ratingCount">8864</span> ratings )</div>
|
||||||
|
<div itemref="offers"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
itemprop="offers"
|
||||||
|
itemid="offers"
|
||||||
|
id="offers"
|
||||||
|
itemscope
|
||||||
|
itemtype="https://schema.org/Offer"
|
||||||
|
>
|
||||||
|
Price: $<span itemprop="price">1.00</span>
|
||||||
|
<meta itemprop="priceCurrency" content="USD"/>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
};
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<!-- Example from https://developer.mozilla.org/en-US/docs/Web/HTML/Microdata -->
|
||||||
|
<div itemscope itemtype="https://schema.org/SoftwareApplication">
|
||||||
|
<span itemprop="name">Game</span> - REQUIRES
|
||||||
|
<span itemprop="operatingSystem">OS</span><br />
|
||||||
|
<link
|
||||||
|
itemprop="applicationCategory"
|
||||||
|
href="https://schema.org/GameApplication"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
itemprop="aggregateRating"
|
||||||
|
itemscope
|
||||||
|
itemtype="https://schema.org/AggregateRating"
|
||||||
|
>
|
||||||
|
RATING:
|
||||||
|
<span itemprop="ratingValue">4.6</span> (
|
||||||
|
<span itemprop="ratingCount">8864</span> ratings )
|
||||||
|
</div>
|
||||||
|
<div itemref="offers" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
itemprop="offers"
|
||||||
|
itemid="offers"
|
||||||
|
id="offers"
|
||||||
|
itemscope
|
||||||
|
itemtype="https://schema.org/Offer"
|
||||||
|
>
|
||||||
|
Price: $<span itemprop="price">1.00</span>
|
||||||
|
<meta itemprop="priceCurrency" content="USD" />
|
||||||
|
</div>
|
||||||
@ -1,15 +0,0 @@
|
|||||||
export default {
|
|
||||||
html: `
|
|
||||||
<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,20 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
<p>4, 12, 60</p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
async test({ component, target, assert }) {
|
||||||
|
component.permutation = [2, 3, 1];
|
||||||
|
await (component.promise1 = Promise.resolve({length: 1, width: 2, height: 3}));
|
||||||
|
try {
|
||||||
|
await (component.promise2 = Promise.reject({length: 97, width: 98, height: 99}));
|
||||||
|
} catch (e) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>2, 11, 2</p>
|
||||||
|
<p>9506, 28811, 98</p>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<script>
|
||||||
|
export let promise1 = {length: 5, width: 3, height: 4};
|
||||||
|
export let promise2 = {length: 12, width: 5, height: 13};
|
||||||
|
export let permutation = [1, 2, 3];
|
||||||
|
|
||||||
|
function calculate(length, width, height) {
|
||||||
|
return {
|
||||||
|
'1-Dimensions': [length, width, height],
|
||||||
|
'2-Dimensions': [length * width, width * height, length * height],
|
||||||
|
'3-Dimensions': [length * width * height, length + width + height, length * width + width * height + length * height]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const th = 'th';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await promise1 then { length, width, height }}
|
||||||
|
{@const { [0]: a, [1]: b, [2]: c } = permutation}
|
||||||
|
{@const { [`${a}-Dimensions`]: { [c - 1]: first }, [`${b}-Dimensions`]: { [b - 1]: second }, [`${c}-Dimensions`]: { [a - 1]: third } } = calculate(length, width, height) }
|
||||||
|
<p>{first}, {second}, {third}</p>
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
{#await promise2 catch { [`leng${th}`]: l, [`wid${th}`]: w, height: h }}
|
||||||
|
{@const [a, b, c] = permutation}
|
||||||
|
{@const { [`${a}-Dimensions`]: { [c - 1]: first }, [`${b}-Dimensions`]: { [b - 1]: second }, [`${c}-Dimensions`]: { [a - 1]: third } } = calculate(l, w, h) }
|
||||||
|
<p>{first}, {second}, {third}</p>
|
||||||
|
{/await}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
<button>6, 12, 8, 24</button>
|
||||||
|
<button>45, 35, 63, 315</button>
|
||||||
|
<button>60, 48, 80, 480</button>
|
||||||
|
`,
|
||||||
|
|
||||||
|
async test({ component, target, assert }) {
|
||||||
|
component.boxes = [{ length: 10, width: 20, height: 30 }];
|
||||||
|
|
||||||
|
assert.htmlEqual(target.innerHTML,
|
||||||
|
'<button>200, 600, 300, 6000</button>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
<script>
|
||||||
|
export let boxes = [
|
||||||
|
{length: 2, width: 3, height: 4},
|
||||||
|
{length: 9, width: 5, height: 7},
|
||||||
|
{length: 10, width: 6, height: 8}
|
||||||
|
];
|
||||||
|
|
||||||
|
function calculate(length, width, height) {
|
||||||
|
return {
|
||||||
|
twoDimensions: {
|
||||||
|
bottomArea: length * width,
|
||||||
|
sideArea1: width * height,
|
||||||
|
sideArea2: length * height
|
||||||
|
},
|
||||||
|
threeDimensions: {
|
||||||
|
volume: length * width * height
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export let dimension = 'Dimensions';
|
||||||
|
function changeDimension() {
|
||||||
|
dimension = 'DIMENSIONS';
|
||||||
|
}
|
||||||
|
|
||||||
|
let area = 'Area';
|
||||||
|
let th = 'th';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each boxes as { [`leng${th}`]: length, [`wid${th}`]: width, height }}
|
||||||
|
{@const {
|
||||||
|
[`two${dimension}`]: areas,
|
||||||
|
[`three${dimension}`]: {
|
||||||
|
volume
|
||||||
|
}
|
||||||
|
} = calculate(length, width, height)}
|
||||||
|
{@const {
|
||||||
|
i = 1,
|
||||||
|
[`bottom${area}`]: bottom,
|
||||||
|
[`side${area}${i++}`]: sideone,
|
||||||
|
[`side${area}${i++}`]: sidetwo
|
||||||
|
} = areas}
|
||||||
|
<button on:click={changeDimension}>{bottom}, {sideone}, {sidetwo}, {volume}</button>
|
||||||
|
{/each}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<div>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
html: `
|
||||||
|
<div> </div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span> </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div> </div>
|
||||||
|
`
|
||||||
|
};
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<script>
|
||||||
|
import Component from './Component.svelte'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Component> </Component>
|
||||||
|
|
||||||
|
<Component>
|
||||||
|
<span> </span>
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<Component>
|
||||||
|
{@html " "}
|
||||||
|
</Component>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
html:'<div>same text</div>',
|
||||||
|
async test({ assert, target }) {
|
||||||
|
await new Promise(f => setTimeout(f, 10));
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<div>same text text</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
let text = 'same';
|
||||||
|
setTimeout(() => {
|
||||||
|
text = 'same text';
|
||||||
|
}, 5);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>{text} text</div>
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
export default {
|
||||||
|
// https://github.com/sveltejs/svelte/issues/6752
|
||||||
|
html: '<img height="100%" width="100%" alt="" />'
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<img height="100%" width="100%" alt="" {...$$restProps} />
|
||||||
Loading…
Reference in new issue