mirror of https://github.com/sveltejs/svelte
commit
a009ee86f5
@ -1,81 +1,63 @@
|
||||
<script>
|
||||
import { createEventDispatcher, onDestroy } from 'svelte';
|
||||
export let showModal; // boolean
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const close = () => dispatch('close');
|
||||
let dialog; // HTMLDialogElement
|
||||
|
||||
let modal;
|
||||
|
||||
const handle_keydown = e => {
|
||||
if (e.key === 'Escape') {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key === 'Tab') {
|
||||
// trap focus
|
||||
const nodes = modal.querySelectorAll('*');
|
||||
const tabbable = Array.from(nodes).filter(n => n.tabIndex >= 0);
|
||||
|
||||
let index = tabbable.indexOf(document.activeElement);
|
||||
if (index === -1 && e.shiftKey) index = 0;
|
||||
|
||||
index += tabbable.length + (e.shiftKey ? -1 : 1);
|
||||
index %= tabbable.length;
|
||||
|
||||
tabbable[index].focus();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const previously_focused = typeof document !== 'undefined' && document.activeElement;
|
||||
|
||||
if (previously_focused) {
|
||||
onDestroy(() => {
|
||||
previously_focused.focus();
|
||||
});
|
||||
}
|
||||
$: if (dialog && showModal) dialog.showModal();
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handle_keydown}/>
|
||||
|
||||
<div class="modal-background" on:click={close}></div>
|
||||
|
||||
<div class="modal" role="dialog" aria-modal="true" bind:this={modal}>
|
||||
<slot name="header"></slot>
|
||||
<hr>
|
||||
<slot></slot>
|
||||
<hr>
|
||||
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<button autofocus on:click={close}>close modal</button>
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<dialog
|
||||
bind:this={dialog}
|
||||
on:close={() => (showModal = false)}
|
||||
on:click|self={() => dialog.close()}
|
||||
>
|
||||
<div on:click|stopPropagation>
|
||||
<slot name="header" />
|
||||
<hr />
|
||||
<slot />
|
||||
<hr />
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<button autofocus on:click={() => dialog.close()}>close modal</button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<style>
|
||||
.modal-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: calc(100vw - 4em);
|
||||
dialog {
|
||||
max-width: 32em;
|
||||
max-height: calc(100vh - 4em);
|
||||
overflow: auto;
|
||||
transform: translate(-50%,-50%);
|
||||
padding: 1em;
|
||||
border-radius: 0.2em;
|
||||
background: white;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
dialog::backdrop {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
dialog > div {
|
||||
padding: 1em;
|
||||
}
|
||||
dialog[open] {
|
||||
animation: zoom 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
@keyframes zoom {
|
||||
from {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
dialog[open]::backdrop {
|
||||
animation: fade 0.2s ease-out;
|
||||
}
|
||||
@keyframes fade {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"title": "Debugging"
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Special tags"
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
export default {
|
||||
warnings: [{
|
||||
filename: 'SvelteComponent.svelte',
|
||||
code: 'css-unused-selector',
|
||||
message: 'Unused CSS selector "img[alt=""]"',
|
||||
start: {
|
||||
character: 87,
|
||||
column: 1,
|
||||
line: 8
|
||||
},
|
||||
end: {
|
||||
character: 98,
|
||||
column: 12,
|
||||
line: 8
|
||||
},
|
||||
pos: 87,
|
||||
frame: `
|
||||
6: }
|
||||
7:
|
||||
8: img[alt=""] {
|
||||
^
|
||||
9: border: 1px solid red;
|
||||
10: }`
|
||||
}]
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
img[alt].svelte-xyz{border:1px solid green}
|
||||
@ -0,0 +1 @@
|
||||
<img alt="a foo" class="svelte-xyz" src="foo.jpg">
|
||||
@ -0,0 +1,11 @@
|
||||
<img src="foo.jpg" alt="a foo" />
|
||||
|
||||
<style>
|
||||
img[alt] {
|
||||
border: 1px solid green;
|
||||
}
|
||||
|
||||
img[alt=""] {
|
||||
border: 1px solid red;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,95 @@
|
||||
/* generated by Svelte vX.Y.Z */
|
||||
import {
|
||||
SvelteComponent,
|
||||
add_render_callback,
|
||||
attr,
|
||||
detach,
|
||||
element,
|
||||
init,
|
||||
insert,
|
||||
listen,
|
||||
noop,
|
||||
safe_not_equal,
|
||||
set_data,
|
||||
space,
|
||||
src_url_equal,
|
||||
text
|
||||
} from "svelte/internal";
|
||||
|
||||
function create_fragment(ctx) {
|
||||
let img;
|
||||
let img_src_value;
|
||||
let t0;
|
||||
let t1;
|
||||
let t2;
|
||||
let t3;
|
||||
let mounted;
|
||||
let dispose;
|
||||
|
||||
return {
|
||||
c() {
|
||||
img = element("img");
|
||||
t0 = space();
|
||||
t1 = text(/*naturalWidth*/ ctx[0]);
|
||||
t2 = text(" x ");
|
||||
t3 = text(/*naturalHeight*/ ctx[1]);
|
||||
if (!src_url_equal(img.src, img_src_value = "something.jpg")) attr(img, "src", img_src_value);
|
||||
if (/*naturalWidth*/ ctx[0] === void 0 || /*naturalHeight*/ ctx[1] === void 0) add_render_callback(() => /*img_load_handler*/ ctx[2].call(img));
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, img, anchor);
|
||||
insert(target, t0, anchor);
|
||||
insert(target, t1, anchor);
|
||||
insert(target, t2, anchor);
|
||||
insert(target, t3, anchor);
|
||||
|
||||
if (!mounted) {
|
||||
dispose = listen(img, "load", /*img_load_handler*/ ctx[2]);
|
||||
mounted = true;
|
||||
}
|
||||
},
|
||||
p(ctx, [dirty]) {
|
||||
if (dirty & /*naturalWidth*/ 1) set_data(t1, /*naturalWidth*/ ctx[0]);
|
||||
if (dirty & /*naturalHeight*/ 2) set_data(t3, /*naturalHeight*/ ctx[1]);
|
||||
},
|
||||
i: noop,
|
||||
o: noop,
|
||||
d(detaching) {
|
||||
if (detaching) detach(img);
|
||||
if (detaching) detach(t0);
|
||||
if (detaching) detach(t1);
|
||||
if (detaching) detach(t2);
|
||||
if (detaching) detach(t3);
|
||||
mounted = false;
|
||||
dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function instance($$self, $$props, $$invalidate) {
|
||||
let { naturalWidth = 0 } = $$props;
|
||||
let { naturalHeight = 0 } = $$props;
|
||||
|
||||
function img_load_handler() {
|
||||
naturalWidth = this.naturalWidth;
|
||||
naturalHeight = this.naturalHeight;
|
||||
$$invalidate(0, naturalWidth);
|
||||
$$invalidate(1, naturalHeight);
|
||||
}
|
||||
|
||||
$$self.$$set = $$props => {
|
||||
if ('naturalWidth' in $$props) $$invalidate(0, naturalWidth = $$props.naturalWidth);
|
||||
if ('naturalHeight' in $$props) $$invalidate(1, naturalHeight = $$props.naturalHeight);
|
||||
};
|
||||
|
||||
return [naturalWidth, naturalHeight, img_load_handler];
|
||||
}
|
||||
|
||||
class Component extends SvelteComponent {
|
||||
constructor(options) {
|
||||
super();
|
||||
init(this, options, instance, create_fragment, safe_not_equal, { naturalWidth: 0, naturalHeight: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
export default Component;
|
||||
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
export let naturalWidth = 0;
|
||||
export let naturalHeight = 0;
|
||||
</script>
|
||||
|
||||
<img src="something.jpg" bind:naturalWidth bind:naturalHeight>
|
||||
|
||||
{naturalWidth} x {naturalHeight}
|
||||
@ -0,0 +1,60 @@
|
||||
/* generated by Svelte vX.Y.Z */
|
||||
import {
|
||||
SvelteComponent,
|
||||
detach,
|
||||
element,
|
||||
init,
|
||||
insert,
|
||||
noop,
|
||||
safe_not_equal,
|
||||
set_data,
|
||||
space,
|
||||
text
|
||||
} from "svelte/internal";
|
||||
|
||||
function create_fragment(ctx) {
|
||||
let h1;
|
||||
let t3;
|
||||
let t4;
|
||||
|
||||
return {
|
||||
c() {
|
||||
h1 = element("h1");
|
||||
h1.textContent = `Hello ${name}!`;
|
||||
t3 = space();
|
||||
t4 = text(/*foo*/ ctx[0]);
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, h1, anchor);
|
||||
insert(target, t3, anchor);
|
||||
insert(target, t4, anchor);
|
||||
},
|
||||
p(ctx, [dirty]) {
|
||||
if (dirty & /*foo*/ 1) set_data(t4, /*foo*/ ctx[0]);
|
||||
},
|
||||
i: noop,
|
||||
o: noop,
|
||||
d(detaching) {
|
||||
if (detaching) detach(h1);
|
||||
if (detaching) detach(t3);
|
||||
if (detaching) detach(t4);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let name = 'world';
|
||||
|
||||
function instance($$self) {
|
||||
let foo;
|
||||
$: foo = name + name;
|
||||
return [foo];
|
||||
}
|
||||
|
||||
class Component extends SvelteComponent {
|
||||
constructor(options) {
|
||||
super();
|
||||
init(this, options, instance, create_fragment, safe_not_equal, {});
|
||||
}
|
||||
}
|
||||
|
||||
export default Component;
|
||||
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let name = 'world';
|
||||
$: foo = name + name;
|
||||
</script>
|
||||
|
||||
<h1>Hello {name}!</h1>
|
||||
{foo}
|
||||
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
async test({ assert, component }) {
|
||||
assert.equal(component.toggle, true);
|
||||
assert.equal(component.offsetHeight, 800);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,18 @@
|
||||
<script>
|
||||
export let offsetHeight;
|
||||
export let offsetWidth;
|
||||
export let toggle = false;
|
||||
$: if (offsetWidth) {
|
||||
toggle = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class:toggle>
|
||||
<div bind:offsetHeight bind:offsetWidth>{offsetHeight}</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.toggle > div {
|
||||
height: 800px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,27 @@
|
||||
<script>
|
||||
import { afterUpdate, onDestroy } from "svelte";
|
||||
|
||||
export let id;
|
||||
export let items;
|
||||
|
||||
let item = $items[id];
|
||||
let selected = true;
|
||||
|
||||
function onClick() {
|
||||
selected = !selected;
|
||||
items.set({});
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
console.log("onDestroy");
|
||||
});
|
||||
|
||||
afterUpdate(() => {
|
||||
console.log("afterUpdate");
|
||||
});
|
||||
</script>
|
||||
|
||||
<button on:click="{onClick}">Click Me</button>
|
||||
{#if selected}
|
||||
<div>{item.id}</div>
|
||||
{/if}
|
||||
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>Click Me</button>
|
||||
<div>1</div>
|
||||
`,
|
||||
async test({ assert, target, window }) {
|
||||
const button = target.querySelector('button');
|
||||
const event = new window.MouseEvent('click');
|
||||
const messages = [];
|
||||
const log = console.log;
|
||||
console.log = msg => messages.push(msg);
|
||||
await button.dispatchEvent(event);
|
||||
console.log = log;
|
||||
assert.deepEqual(messages, ['afterUpdate', 'onDestroy']);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
import { writable } from 'svelte/store';
|
||||
import Component from "./Component.svelte";
|
||||
|
||||
let items = writable({ 1: { id: 1 } });
|
||||
</script>
|
||||
|
||||
{#each Object.values($items) as item (item.id)}
|
||||
<Component id="{item.id}" {items} />
|
||||
{/each}
|
||||
@ -0,0 +1,11 @@
|
||||
export default {
|
||||
props: {
|
||||
inert: true
|
||||
},
|
||||
test({ assert, target, component }) {
|
||||
const div = target.querySelector('div');
|
||||
assert.ok(div.inert);
|
||||
component.inert = false;
|
||||
assert.ok(!div.inert);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let inert;
|
||||
</script>
|
||||
|
||||
<div {inert}>some div <button>click</button></div>
|
||||
@ -0,0 +1,11 @@
|
||||
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);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let itemscope;
|
||||
</script>
|
||||
|
||||
<div {itemscope} />
|
||||
@ -0,0 +1 @@
|
||||
<a href="/foo" aria-label="baz"></a>
|
||||
@ -0,0 +1 @@
|
||||
[]
|
||||
@ -0,0 +1,17 @@
|
||||
<!-- VALID -->
|
||||
<input />
|
||||
<input tabindex="0" />
|
||||
<input aria-activedescendant="some-id" />
|
||||
<input aria-activedescendant="some-id" tabindex={0} />
|
||||
<input aria-activedescendant="some-id" tabindex={1} />
|
||||
<input aria-activedescendant="some-id" tabindex="0" />
|
||||
<input aria-activedescendant="some-id" tabindex={-1} />
|
||||
<input aria-activedescendant="some-id" tabindex="-1" />
|
||||
|
||||
<div />
|
||||
<div aria-activedescendant="some-id" role="tablist" tabindex={-1} />
|
||||
<div aria-activedescendant="some-id" role="tablist" tabindex="-1" />
|
||||
|
||||
<!-- INVALID -->
|
||||
<div aria-activedescendant="some-id" />
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"code": "a11y-aria-activedescendant-has-tabindex",
|
||||
"end": {
|
||||
"column": 36,
|
||||
"line": 16
|
||||
},
|
||||
"message": "A11y: Elements with attribute aria-activedescendant should have tabindex value",
|
||||
"start": {
|
||||
"column": 5,
|
||||
"line": 16
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -1,47 +1,38 @@
|
||||
[
|
||||
{
|
||||
"code": "a11y-incorrect-aria-attribute-type",
|
||||
"message": "A11y: The value of 'aria-valuemax' must be of type number",
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 5,
|
||||
"character": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 24,
|
||||
"character": 24
|
||||
},
|
||||
"pos": 5
|
||||
},
|
||||
{
|
||||
"code": "a11y-incorrect-aria-attribute-type",
|
||||
"message": "A11y: The value of 'aria-valuemax' must be of type number",
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 5,
|
||||
"character": 33
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 23,
|
||||
"character": 51
|
||||
},
|
||||
"pos": 33
|
||||
},
|
||||
{
|
||||
"code": "a11y-incorrect-aria-attribute-type",
|
||||
"message": "A11y: The value of 'aria-valuemax' must be of type number",
|
||||
"start": {
|
||||
"line": 5,
|
||||
"column": 5,
|
||||
"character": 119
|
||||
},
|
||||
"end": {
|
||||
"line": 5,
|
||||
"column": 18,
|
||||
"character": 132
|
||||
},
|
||||
"pos": 119
|
||||
}
|
||||
]
|
||||
{
|
||||
"code": "a11y-incorrect-aria-attribute-type",
|
||||
"message": "A11y: The value of 'aria-valuemax' must be of type number",
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 24
|
||||
}
|
||||
},
|
||||
{
|
||||
"code": "a11y-incorrect-aria-attribute-type",
|
||||
"message": "A11y: The value of 'aria-valuemax' must be of type number",
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"code": "a11y-incorrect-aria-attribute-type",
|
||||
"message": "A11y: The value of 'aria-valuemax' must be of type number",
|
||||
"start": {
|
||||
"line": 5,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 5,
|
||||
"column": 18
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -1,17 +1,14 @@
|
||||
[
|
||||
{
|
||||
"code": "a11y-incorrect-aria-attribute-type",
|
||||
"message": "A11y: The value of 'aria-label' must be of type string",
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 5,
|
||||
"character": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 15,
|
||||
"character": 15
|
||||
},
|
||||
"pos": 5
|
||||
}
|
||||
]
|
||||
{
|
||||
"code": "a11y-incorrect-aria-attribute-type",
|
||||
"message": "A11y: The value of 'aria-label' must be of type string",
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 15
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue