mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
We now have an element-registry.sky module that exposes API to register elements with attributes and event handlers. Right now it's an API on the module itself, eventually the module will export an ElementRegistry object since we'll need one per module once the custom element registry is one per module. R=abarth@chromium.org, ojan@chromium.org Review URL: https://codereview.chromium.org/856693002
194 lines
4.9 KiB
Plaintext
194 lines
4.9 KiB
Plaintext
<!--
|
|
// Copyright 2014 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
-->
|
|
<import src="sky-binder.sky" as="binder" />
|
|
<import src="element-registry.sky" as="registry" />
|
|
<script>
|
|
function parseAttributeSpec(registration, definition) {
|
|
var spec = definition.getAttribute('attributes');
|
|
|
|
if (!spec)
|
|
return;
|
|
|
|
var attributeTokens = spec.split(',');
|
|
|
|
for (var i = 0; i < attributeTokens.length; ++i) {
|
|
var parts = attributeTokens[i].split(':');
|
|
|
|
if (parts.length != 2) {
|
|
console.error('Invalid attribute spec "' + spec + '", attributes must' +
|
|
' be {name}:{type}, where type is one of boolean, number or' +
|
|
' string.');
|
|
continue;
|
|
}
|
|
|
|
var name = parts[0].trim();
|
|
var type = parts[1].trim();
|
|
|
|
registration.defineAttribute(name, type);
|
|
}
|
|
}
|
|
|
|
function parseEventHandlers(registration, definition) {
|
|
var eventHandlers = [];
|
|
var attributes = definition.getAttributes();
|
|
|
|
for (var i = 0; i < attributes.length; i++) {
|
|
var attr = attributes[i];
|
|
var name = attr.name;
|
|
var value = attr.value;
|
|
|
|
if (name.startsWith('on-')) {
|
|
registration.eventHandlers.set(name.substring(3), value);
|
|
}
|
|
}
|
|
}
|
|
|
|
class SkyElement extends HTMLElement {
|
|
|
|
static register() {
|
|
var definition = document.currentScript.parentNode;
|
|
|
|
if (definition.localName !== 'sky-element') {
|
|
throw new Error('register() calls must be inside a <sky-element>.');
|
|
}
|
|
|
|
var tagName = definition.getAttribute('name');
|
|
if (!tagName) {
|
|
throw new Error('<sky-element> must have a name.');
|
|
}
|
|
|
|
var registration = registry.registerElement(tagName);
|
|
|
|
parseAttributeSpec(registration, definition);
|
|
parseEventHandlers(registration, definition);
|
|
|
|
registration.template = definition.querySelector('template');
|
|
|
|
registration.synthesizeAttributes(this.prototype);
|
|
|
|
return document.registerElement(tagName, {
|
|
prototype: this.prototype,
|
|
});
|
|
}
|
|
|
|
created() {
|
|
// override
|
|
}
|
|
|
|
attached() {
|
|
// override
|
|
}
|
|
|
|
detached() {
|
|
// override
|
|
}
|
|
|
|
attributeChanged(attrName, oldValue, newValue) {
|
|
// override
|
|
}
|
|
|
|
shadowRootReady() {
|
|
// override
|
|
}
|
|
|
|
createdCallback() {
|
|
this.isAttached = false;
|
|
this.propertyBindings = null;
|
|
this.dirtyPropertyBindings = null;
|
|
this.created();
|
|
|
|
Object.preventExtensions(this);
|
|
|
|
// Invoke attributeChanged callback when element is first created too.
|
|
var attributes = this.getAttributes();
|
|
for (var i = 0; i < attributes.length; ++i) {
|
|
var attribute = attributes[i];
|
|
this.attributeChangedCallback(attribute.name, null, attribute.value);
|
|
}
|
|
|
|
var registration = registry.getRegistration(this.localName);
|
|
registration.addInstanceEventListeners(this);
|
|
}
|
|
|
|
attachedCallback() {
|
|
if (!this.shadowRoot) {
|
|
var registration = registry.getRegistration(this.localName);
|
|
if (registration.template) {
|
|
var shadow = this.ensureShadowRoot();
|
|
var instance = binder.createInstance(registration.template, this);
|
|
shadow.appendChild(instance.fragment);
|
|
this.shadowRootReady();
|
|
}
|
|
}
|
|
this.attached();
|
|
this.isAttached = true;
|
|
}
|
|
|
|
detachedCallback() {
|
|
this.detached();
|
|
this.isAttached = false;
|
|
}
|
|
|
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
this.attributeChanged(name, oldValue, newValue);
|
|
var registration = registry.getRegistration(this.localName);
|
|
var converter = registration.attributes.get(name);
|
|
if (converter) {
|
|
this.notifyPropertyChanged(name, converter(oldValue),
|
|
converter(newValue));
|
|
}
|
|
}
|
|
|
|
notifyPropertyChanged(name, oldValue, newValue) {
|
|
if (oldValue == newValue)
|
|
return;
|
|
var notifier = Object.getNotifier(this);
|
|
notifier.notify({
|
|
type: 'update',
|
|
name: name,
|
|
oldValue: oldValue,
|
|
});
|
|
var handler = this[name + 'Changed'];
|
|
if (typeof handler == 'function')
|
|
handler.call(this, oldValue, newValue);
|
|
this.schedulePropertyBindingUpdate(name);
|
|
}
|
|
|
|
addPropertyBinding(name, binding) {
|
|
if (!this.propertyBindings)
|
|
this.propertyBindings = new Map();
|
|
this.propertyBindings.set(name, binding);
|
|
}
|
|
|
|
getPropertyBinding(name) {
|
|
if (!this.propertyBindings)
|
|
return null;
|
|
return this.propertyBindings.get(name);
|
|
}
|
|
|
|
schedulePropertyBindingUpdate(name) {
|
|
if (!this.dirtyPropertyBindings) {
|
|
this.dirtyPropertyBindings = new Set();
|
|
Promise.resolve().then(this.updatePropertyBindings.bind(this));
|
|
}
|
|
this.dirtyPropertyBindings.add(name);
|
|
}
|
|
|
|
updatePropertyBindings() {
|
|
for (var name of this.dirtyPropertyBindings) {
|
|
var binding = this.getPropertyBinding(name);
|
|
if (binding) {
|
|
binding.setValue(this[name]);
|
|
binding.discardChanges();
|
|
}
|
|
}
|
|
this.dirtyPropertyBindings = null;
|
|
}
|
|
};
|
|
|
|
module.exports = SkyElement;
|
|
</script>
|