Skip to content

Commit

Permalink
WIP [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
sukima committed Oct 23, 2020
1 parent 05f8845 commit 3b8a560
Show file tree
Hide file tree
Showing 9 changed files with 486 additions and 24 deletions.
13 changes: 4 additions & 9 deletions index.html → examples/1.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FancyPants Sample</title>
<title>FancyPants Example One</title>
<script type="module">

import Component from './lib/component.js';
import { tracked } from './lib/tracking.js';
import Component from '../lib/component.js';
import { tracked } from '../lib/tracking.js';

class FooBar extends Component {
foo = tracked(0);
Expand All @@ -33,7 +33,7 @@
</head>
<body>

<h1>FancyPants Sample</h1>
<h1>FancyPants Example One</h1>

<p><foo-bar></foo-bar></p>
<p><foo-bar></foo-bar></p>
Expand All @@ -42,11 +42,6 @@ <h1>FancyPants Sample</h1>
<button class="go">+1</button>
<output class="foo"></output>
<output class="bar"></output>
<ol class="list">
<li class="index">One</li>
<li class="index">Two</li>
<li class="index">Three</li>
</ol>
</template>

</body>
Expand Down
49 changes: 49 additions & 0 deletions examples/2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FancyPants Example Two</title>
<script type="module">

import Component from '../lib/component.js';
import { tracked } from '../lib/tracking.js';

class FooBar extends Component {
foo = tracked(0);

get bar() {
return `bar-${this.foo}`;
}

constructor() {
super();
this.shadow.querySelector('.go')
.addEventListener('click', () => { this.foo += 1 });
}

render() {
this.shadow.querySelector('.foo').value = this.foo;
this.shadow.querySelector('.bar').value = this.bar;
}

static template = `
<button class="go">+1</button>
<output class="foo"></output>
<output class="bar"></output>
`;

}

FooBar.register();

</script>
</head>
<body>

<h1>FancyPants Example Two</h1>

<p><foo-bar></foo-bar></p>
<p><foo-bar></foo-bar></p>

</body>
</html>
49 changes: 49 additions & 0 deletions examples/3.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FancyPants Example Three</title>
<script type="module">

import Component from '../lib/component.js';
import { tracked } from '../lib/tracking.js';

class FooBar extends Component {
foo = tracked(0);

get bar() {
return `bar-${this.foo}`;
}

constructor() {
super();
this.shadow.querySelector('.go')
.addEventListener('click', () => { this.foo += 1 });
}

render() {
this.shadow.querySelector('.foo').value = this.foo;
this.shadow.querySelector('.bar').value = this.bar;
}

}

FooBar.register('#different-id');

</script>
</head>
<body>

<h1>FancyPants Example Three</h1>

<p><foo-bar></foo-bar></p>
<p><foo-bar></foo-bar></p>

<template id="different-id">
<button class="go">+1</button>
<output class="foo"></output>
<output class="bar"></output>
</template>

</body>
</html>
51 changes: 51 additions & 0 deletions examples/4.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FancyPants Example Four</title>
<script type="module">

import Component from '../lib/component.js';
import { tracked, activateTracking } from '../lib/tracking.js';

const globalTrackable = activateTracking({
foo: tracked(0),
});

class FooBar extends Component {

get bar() {
return `bar-${globalTrackable.foo}`;
}

constructor() {
super();
this.shadow.querySelector('.go')
.addEventListener('click', () => { globalTrackable.foo += 1 });
}

render() {
this.shadow.querySelector('.foo').value = globalTrackable.foo;
this.shadow.querySelector('.bar').value = this.bar;
}
}

FooBar.register();

</script>
</head>
<body>

<h1>FancyPants Example Four</h1>

<p><foo-bar></foo-bar></p>
<p><foo-bar></foo-bar></p>

<template id="foo-bar">
<button class="go">+1</button>
<output class="foo"></output>
<output class="bar"></output>
</template>

</body>
</html>
60 changes: 60 additions & 0 deletions examples/5.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FancyPants Example Five</title>
<script type="module">

import Component from '../lib/component.js';
import { tracked, dirtyTag, consumeTag, createTag } from '../lib/tracking.js';

const globalTag = createTag();
let globalValue = 0;

function updateGlobalValue() {
dirtyTag(globalTag);
globalValue++;
}

function getGlobalValue() {
consumeTag(globalTag);
return globalValue;
}

class FooBar extends Component {

get bar() {
return `bar-${getGlobalValue()}`;
}

constructor() {
super();
this.shadow.querySelector('.go')
.addEventListener('click', updateGlobalValue);
}

render() {
this.shadow.querySelector('.foo').value = getGlobalValue();
this.shadow.querySelector('.bar').value = this.bar;
}
}

FooBar.register();

</script>
</head>
<body>

<h1>FancyPants Example Five</h1>

<p><foo-bar></foo-bar></p>
<p><foo-bar></foo-bar></p>

<template id="foo-bar">
<button class="go">+1</button>
<output class="foo"></output>
<output class="bar"></output>
</template>

</body>
</html>
122 changes: 122 additions & 0 deletions examples/6.html

Large diffs are not rendered by default.

131 changes: 131 additions & 0 deletions examples/7.html

Large diffs are not rendered by default.

29 changes: 20 additions & 9 deletions lib/component.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { scheduleRender, registerRenderer, unregisterRenderer } from './renderer.js';
import { activateTracking, memoizeFunction } from './tracking.js';
import { activateTracking, memoizeFunction, createTag, dirtyTag, consumeTag } from './tracking.js';

const ATTRIBUTE_TAGS = Symbol('attribute tags');
const RENDER = Symbol('memoized render');
const templates = new Map();

function dasherize(str) {
return str.replace(/([a-z\d])([A-Z])/g, '$1-$2').toLowerCase();
}

function makeTemplateElement(html = '') {
let element = document.createElement('template');
element.innerHTML = html;
return element;
}

export default class Component extends HTMLElement {

constructor() {
Expand All @@ -16,6 +23,9 @@ export default class Component extends HTMLElement {
this.shadow = this.attachShadow({ mode: 'open' });
this.shadow.appendChild(template.content.cloneNode(true));
this[RENDER] = memoizeFunction(() => this.render());
this[ATTRIBUTE_TAGS] = new Map(
(this.constructor.observedAttributes ?? []).map(i => [i, createTag()])
);
}

connectedCallback() {
Expand All @@ -28,8 +38,13 @@ export default class Component extends HTMLElement {
unregisterRenderer(this[RENDER]);
}

attributeChangedCallback() {
scheduleRender();
attributeChangedCallback(name) {
dirtyTag(this[ATTRIBUTE_TAGS].get(name));
}

getAttribute(name) {
consumeTag(this[ATTRIBUTE_TAGS].get(name));
return super.getAttribute(name);
}

render() {}
Expand All @@ -41,13 +56,9 @@ export default class Component extends HTMLElement {
} else if (templatable instanceof Element) {
templateFactory = () => templatable;
} else if (this.template) {
templateFactory = () => {
let element = document.createElement('template');
element.innerHTML = this.template;
return element;
};
templateFactory = () => makeTemplateElement(this.template);
} else {
templateFactory = () => queryable.querySelector(`#${this.tagName}`);
templateFactory = () => queryable.querySelector(`template#${this.tagName}`) ?? makeTemplateElement();
}
templates.set(this.tagName, templateFactory());
customElements.define(this.tagName, this);
Expand Down
6 changes: 0 additions & 6 deletions lib/renderer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { setOnTagDirtied } from './tracking.js';

let renderScheduled = false;
let currentlyRendering = false;
const renderOperations = new Set();

export function registerRenderer(renderFn) {
Expand All @@ -13,21 +12,16 @@ export function unregisterRenderer(renderFn) {
}

export function scheduleRender() {
if (currentlyRendering) {
throw new Error('scheduling a render while currently rendering is not supported');
}
if (renderScheduled) { return; }
renderScheduled = true;
Promise.resolve().then(executeRenderOperations);
}

function executeRenderOperations() {
currentlyRendering = true;
try {
renderOperations.forEach(i => i());
} finally {
renderScheduled = false;
currentlyRendering = false;
}
}

Expand Down

0 comments on commit 3b8a560

Please sign in to comment.