mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Now instead of calling addEventListener inside the created() callback you just
add on-{eventName} attributes to the <sky-element> around your element
definition. The attribute defines what method on your element to call when the
event with {eventName} is dispatched through your element.
Implementing this required refactoring how sky-element.sky stored information
from each registered element. Now instead of storing just a map of templates
we store a map of ElementRegistration objects which have metadata about the
element. Future patches will combine this system with the element registration
system in sky-binder.
R=ojan@chromium.org
Review URL: https://codereview.chromium.org/845283003
225 lines
5.7 KiB
Plaintext
225 lines
5.7 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" />
|
|
<script>
|
|
var attributeConverters = {
|
|
boolean: function(value) {
|
|
if (typeof value == 'string')
|
|
return value == 'true';
|
|
return !!value;
|
|
},
|
|
number: function(value) {
|
|
return Number(value);
|
|
},
|
|
string: function(value) {
|
|
if (value === null)
|
|
return '';
|
|
return String(value);
|
|
},
|
|
};
|
|
|
|
function parseAttributeSpec(spec) {
|
|
var attributes = new Map();
|
|
var attributeTokens = (spec || '').split(',');
|
|
|
|
for (var i = 0; i < attributeTokens.length; ++i) {
|
|
var parts = attributeTokens[i].split(':');
|
|
var name = parts[0].trim();
|
|
var type = (parts[1] || '').trim();
|
|
var converter = attributeConverters[type] || attributeConverters.string;
|
|
|
|
attributes.set(name, converter);
|
|
}
|
|
|
|
return attributes;
|
|
}
|
|
|
|
function collectEventHandlers(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-')) {
|
|
eventHandlers.push(name.substring(3));
|
|
}
|
|
}
|
|
|
|
return eventHandlers;
|
|
}
|
|
|
|
function eventHandlerCallback(event) {
|
|
var element = event.currentTarget;
|
|
var registration = registrations.get(element.localName);
|
|
var method = registration.getEventHandler(event.type);
|
|
var handler = element[method];
|
|
if (handler instanceof Function)
|
|
return handler.call(element, event);
|
|
}
|
|
|
|
class ElementRegistration {
|
|
constructor(definition) {
|
|
this.definition = definition;
|
|
this.tagName = definition.getAttribute('name');
|
|
this.attributes = parseAttributeSpec(definition.getAttribute('attributes'));
|
|
this.eventHandlers = collectEventHandlers(definition);
|
|
this.template = definition.querySelector('template');
|
|
Object.preventExtensions(this);
|
|
}
|
|
|
|
getEventHandler(eventName) {
|
|
return this.definition.getAttribute('on-' + eventName);
|
|
}
|
|
|
|
getAttributeNames() {
|
|
// TODO(esprehn): We can replace this method with
|
|
// Array.from(registration.attributes) once we turn that on.
|
|
var names = []
|
|
this.attributes.forEach(function(converter, name) {
|
|
names.push(name);
|
|
});
|
|
return names;
|
|
}
|
|
|
|
synthesizeAttributes(prototype) {
|
|
this.attributes.forEach(function(converter, name) {
|
|
Object.defineProperty(prototype, name, {
|
|
get: function() {
|
|
return converter(this.getAttribute(name));
|
|
},
|
|
set: function(newValue) {
|
|
this.setAttribute(name, converter(newValue));
|
|
},
|
|
enumerable: true,
|
|
configurable: true,
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
var registrations = new Map();
|
|
|
|
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 registration = new ElementRegistration(definition);
|
|
|
|
if (!registration.tagName) {
|
|
throw new Error('<sky-element> must have a name.');
|
|
}
|
|
|
|
if (registrations.has(registration.tagName)) {
|
|
throw new Error('Duplicate registration for tag name: ' +
|
|
registration.tagName);
|
|
}
|
|
|
|
registration.synthesizeAttributes(this.prototype);
|
|
|
|
// TODO(esprehn): Combine the two element registries here and in sky binder.
|
|
binder.registerElement(registration.tagName, {
|
|
attributeNames: registration.getAttributeNames(),
|
|
});
|
|
|
|
registrations.set(registration.tagName, registration);
|
|
return document.registerElement(registration.tagName, {
|
|
prototype: this.prototype,
|
|
});
|
|
}
|
|
|
|
created() {
|
|
// override
|
|
}
|
|
|
|
attached() {
|
|
// override
|
|
}
|
|
|
|
detached() {
|
|
// override
|
|
}
|
|
|
|
attributeChanged(attrName, oldValue, newValue) {
|
|
// override
|
|
}
|
|
|
|
shadowRootReady() {
|
|
// override
|
|
}
|
|
|
|
createdCallback() {
|
|
this.isAttached = false;
|
|
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 = registrations.get(this.localName);
|
|
for (var i = 0; i < registration.eventHandlers.length; ++i) {
|
|
var eventName = registration.eventHandlers[i];
|
|
this.addEventListener(eventName, eventHandlerCallback);
|
|
}
|
|
}
|
|
|
|
attachedCallback() {
|
|
if (!this.shadowRoot) {
|
|
var registration = registrations.get(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 = registrations.get(this.localName);
|
|
var converter = registration.attributes.get(name);
|
|
if (converter) {
|
|
this.notifyPropertyChanged(name, converter(oldValue),
|
|
converter(newValue));
|
|
}
|
|
}
|
|
|
|
notifyPropertyChanged(name, oldValue, newValue) {
|
|
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);
|
|
}
|
|
};
|
|
|
|
module.exports = SkyElement;
|
|
</script>
|