For simplicity, replace Polymer.ElementMixin's static config property with properties and observers

This commit is contained in:
Steven Orvell
2017-02-23 19:39:53 -08:00
parent b532d267d2
commit 796a5d5809
11 changed files with 338 additions and 347 deletions

View File

@@ -73,63 +73,60 @@ is false, `selected` is a property representing the last selected item. When
return class extends superClass {
static get config() {
static get properties() {
return {
properties: {
/**
* An array containing items from which selection will be made.
*/
items: {
type: Array,
},
/**
* When `true`, multiple items may be selected at once (in this case,
* `selected` is an array of currently selected items). When `false`,
* only one item may be selected at a time.
*/
multi: {
type: Boolean,
value: false,
},
/**
* When `multi` is true, this is an array that contains any selected.
* When `multi` is false, this is the currently selected item, or `null`
* if no item is selected.
*/
selected: {
type: Object,
notify: true
},
/**
* When `multi` is false, this is the currently selected item, or `null`
* if no item is selected.
*/
selectedItem: {
type: Object,
notify: true
},
/**
* When `true`, calling `select` on an item that is already selected
* will deselect the item.
*/
toggle: {
type: Boolean,
value: false
}
/**
* An array containing items from which selection will be made.
*/
items: {
type: Array,
},
observers: ['__updateSelection(multi, items.*)'],
/**
* When `true`, multiple items may be selected at once (in this case,
* `selected` is an array of currently selected items). When `false`,
* only one item may be selected at a time.
*/
multi: {
type: Boolean,
value: false,
},
/**
* When `multi` is true, this is an array that contains any selected.
* When `multi` is false, this is the currently selected item, or `null`
* if no item is selected.
*/
selected: {
type: Object,
notify: true
},
/**
* When `multi` is false, this is the currently selected item, or `null`
* if no item is selected.
*/
selectedItem: {
type: Object,
notify: true
},
/**
* When `true`, calling `select` on an item that is already selected
* will deselect the item.
*/
toggle: {
type: Boolean,
value: false
}
}
}
static get observers() {
return ['__updateSelection(multi, items.*)']
}
constructor() {

View File

@@ -33,7 +33,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
static get template() { return null; }
static get config() {
static get properties() {
return {
@@ -45,31 +45,27 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
* @event dom-change
*/
properties: {
/**
* A boolean indicating whether this template should stamp.
*/
'if': {
type: Boolean,
observer: '__debounceRender'
},
/**
* When true, elements will be removed from DOM and discarded when `if`
* becomes false and re-created and added back to the DOM when `if`
* becomes true. By default, stamped elements will be hidden but left
* in the DOM when `if` becomes false, which is generally results
* in better performance.
*/
restamp: {
type: Boolean,
observer: '__debounceRender'
}
/**
* A boolean indicating whether this template should stamp.
*/
'if': {
type: Boolean,
observer: '__debounceRender'
},
/**
* When true, elements will be removed from DOM and discarded when `if`
* becomes false and re-created and added back to the DOM when `if`
* becomes true. By default, stamped elements will be hidden but left
* in the DOM when `if` becomes false, which is generally results
* in better performance.
*/
restamp: {
type: Boolean,
observer: '__debounceRender'
}
};
}
}

View File

@@ -111,154 +111,149 @@ Then the `observe` property should be configured as follows:
static get template() { return null; }
static get config() {
static get properties() {
/**
* Fired whenever DOM is added or removed by this template (by
* default, rendering occurs lazily). To force immediate rendering, call
* `render`.
*
* @event dom-change
*/
return {
/**
* Fired whenever DOM is added or removed by this template (by
* default, rendering occurs lazily). To force immediate rendering, call
* `render`.
*
* @event dom-change
* An array containing items determining how many instances of the template
* to stamp and that that each template instance should bind to.
*/
properties: {
/**
* An array containing items determining how many instances of the template
* to stamp and that that each template instance should bind to.
*/
items: {
type: Array
},
/**
* The name of the variable to add to the binding scope for the array
* element associated with a given template instance.
*/
as: {
type: String,
value: 'item'
},
/**
* The name of the variable to add to the binding scope with the index
* for the inst. If `sort` is provided, the index will reflect the
* sorted order (rather than the original array order).
*/
indexAs: {
type: String,
value: 'index'
},
/**
* The name of the variable to add to the binding scope with the index
* for the inst. If `sort` is provided, the index will reflect the
* sorted order (rather than the original array order).
*/
itemsIndexAs: {
type: String,
value: 'itemsIndex'
},
/**
* A function that should determine the sort order of the items. This
* property should either be provided as a string, indicating a method
* name on the element's host, or else be an actual function. The
* function should match the sort function passed to `Array.sort`.
* Using a sort function has no effect on the underlying `items` array.
*/
sort: {
type: Function,
observer: '__sortChanged'
},
/**
* A function that can be used to filter items out of the view. This
* property should either be provided as a string, indicating a method
* name on the element's host, or else be an actual function. The
* function should match the sort function passed to `Array.filter`.
* Using a filter function has no effect on the underlying `items` array.
*/
filter: {
type: Function,
observer: '__filterChanged'
},
/**
* When using a `filter` or `sort` function, the `observe` property
* should be set to a space-separated list of the names of item
* sub-fields that should trigger a re-sort or re-filter when changed.
* These should generally be fields of `item` that the sort or filter
* function depends on.
*/
observe: {
type: String,
observer: '__observeChanged'
},
/**
* When using a `filter` or `sort` function, the `delay` property
* determines a debounce time after a change to observed item
* properties that must pass before the filter or sort is re-run.
* This is useful in rate-limiting shuffing of the view when
* item changes may be frequent.
*/
delay: Number,
/**
* Count of currently rendered items after `filter` (if any) has been applied.
* If "chunking mode" is enabled, `renderedItemCount` is updated each time a
* set of template instances is rendered.
*
*/
renderedItemCount: {
type: Number,
notify: true,
readOnly: true
},
/**
* Defines an initial count of template instances to render after setting
* the `items` array, before the next paint, and puts the `dom-repeat`
* into "chunking mode". The remaining items will be created and rendered
* incrementally at each animation frame therof until all instances have
* been rendered.
*/
initialCount: {
type: Number,
observer: '__initializeChunking'
},
/**
* When `initialCount` is used, this property defines a frame rate to
* target by throttling the number of instances rendered each frame to
* not exceed the budget for the target frame rate. Setting this to a
* higher number will allow lower latency and higher throughput for
* things like event handlers, but will result in a longer time for the
* remaining items to complete rendering.
*/
targetFramerate: {
type: Number,
value: 20
},
_targetFrameTime: {
type: Number,
computed: '__computeFrameTime(targetFramerate)'
}
items: {
type: Array
},
observers: [
'__itemsChanged(items.*)'
]
/**
* The name of the variable to add to the binding scope for the array
* element associated with a given template instance.
*/
as: {
type: String,
value: 'item'
},
/**
* The name of the variable to add to the binding scope with the index
* for the inst. If `sort` is provided, the index will reflect the
* sorted order (rather than the original array order).
*/
indexAs: {
type: String,
value: 'index'
},
/**
* The name of the variable to add to the binding scope with the index
* for the inst. If `sort` is provided, the index will reflect the
* sorted order (rather than the original array order).
*/
itemsIndexAs: {
type: String,
value: 'itemsIndex'
},
/**
* A function that should determine the sort order of the items. This
* property should either be provided as a string, indicating a method
* name on the element's host, or else be an actual function. The
* function should match the sort function passed to `Array.sort`.
* Using a sort function has no effect on the underlying `items` array.
*/
sort: {
type: Function,
observer: '__sortChanged'
},
/**
* A function that can be used to filter items out of the view. This
* property should either be provided as a string, indicating a method
* name on the element's host, or else be an actual function. The
* function should match the sort function passed to `Array.filter`.
* Using a filter function has no effect on the underlying `items` array.
*/
filter: {
type: Function,
observer: '__filterChanged'
},
/**
* When using a `filter` or `sort` function, the `observe` property
* should be set to a space-separated list of the names of item
* sub-fields that should trigger a re-sort or re-filter when changed.
* These should generally be fields of `item` that the sort or filter
* function depends on.
*/
observe: {
type: String,
observer: '__observeChanged'
},
/**
* When using a `filter` or `sort` function, the `delay` property
* determines a debounce time after a change to observed item
* properties that must pass before the filter or sort is re-run.
* This is useful in rate-limiting shuffing of the view when
* item changes may be frequent.
*/
delay: Number,
/**
* Count of currently rendered items after `filter` (if any) has been applied.
* If "chunking mode" is enabled, `renderedItemCount` is updated each time a
* set of template instances is rendered.
*
*/
renderedItemCount: {
type: Number,
notify: true,
readOnly: true
},
/**
* Defines an initial count of template instances to render after setting
* the `items` array, before the next paint, and puts the `dom-repeat`
* into "chunking mode". The remaining items will be created and rendered
* incrementally at each animation frame therof until all instances have
* been rendered.
*/
initialCount: {
type: Number,
observer: '__initializeChunking'
},
/**
* When `initialCount` is used, this property defines a frame rate to
* target by throttling the number of instances rendered each frame to
* not exceed the budget for the target frame rate. Setting this to a
* higher number will allow lower latency and higher throughput for
* things like event handlers, but will result in a longer time for the
* remaining items to complete rendering.
*/
targetFramerate: {
type: Number,
value: 20
},
_targetFrameTime: {
type: Number,
computed: '__computeFrameTime(targetFramerate)'
}
}
}
static get observers() {
return [ '__itemsChanged(items.*)' ]
}
constructor() {
super();
this.__instances = [];

View File

@@ -123,18 +123,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
function GenerateClassFromInfo(info, Base) {
let config = {
properties: info.properties,
observers: info.observers,
generatedFrom: info
};
let registered = false;
class PolymerGenerated extends Base {
static get config() {
return config;
static get properties() {
return info.properties;
}
static get observers() {
return info.observers;
}
static get template() {
@@ -217,6 +215,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}
}
PolymerGenerated.generatedFrom = info
for (let p in info) {
// NOTE: cannot copy `metaProps` methods onto prototype at least because
// `super.ready` must be called and is not included in the user fn.

View File

@@ -24,21 +24,37 @@ Polymer.ElementMixin = Polymer.dedupingMixin(function(base) {
let caseMap = Polymer.CaseMap;
/**
* Returns the config object specifically on `klass`. Use for:
* Returns the `properties` object specifically on `klass`. Use for:
* (1) super chain mixes togther to make `propertiesForClass` which is
* then used to make `observedAttributes`.
* (2) properties effects and observers are created from it at `finalize` time.
* @param {HTMLElement} klass
* @private
*/
function ownConfigForClass(klass) {
function ownPropertiesForClass(klass) {
if (!klass.hasOwnProperty(
goog.reflect.objectProperty('__ownConfig', klass))) {
klass.__ownConfig =
klass.hasOwnProperty(goog.reflect.objectProperty('config', klass)) ?
klass.config : {};
goog.reflect.objectProperty('__ownProperties', klass))) {
klass.__ownProperties =
klass.hasOwnProperty(goog.reflect.objectProperty('properties', klass)) ?
klass.properties : {};
}
return klass.__ownConfig;
return klass.__ownProperties;
}
/**
* Returns the `observers` array specifically on `klass`. Use for
* setting up observers.
* @param {HTMLElement} klass
* @private
*/
function ownObserversForClass(klass) {
if (!klass.hasOwnProperty(
goog.reflect.objectProperty('__ownObservers', klass))) {
klass.__ownObservers =
klass.hasOwnProperty(goog.reflect.objectProperty('observers', klass)) ?
klass.observers : [];
}
return klass.__ownObservers;
}
/**
@@ -71,7 +87,7 @@ Polymer.ElementMixin = Polymer.dedupingMixin(function(base) {
if (!klass.hasOwnProperty(
goog.reflect.objectProperty('__classProperties', klass))) {
klass.__classProperties =
flattenProperties({}, ownConfigForClass(klass).properties);
flattenProperties({}, ownPropertiesForClass(klass));
let superCtor = Object.getPrototypeOf(klass.prototype).constructor;
if (superCtor.prototype instanceof PolymerElement) {
klass.__classProperties = Polymer.mixin(
@@ -144,9 +160,13 @@ Polymer.ElementMixin = Polymer.dedupingMixin(function(base) {
goog.reflect.objectProperty('is', klass)) && klass.is) {
Polymer.telemetry.register(proto);
}
let config = ownConfigForClass(klass);
if (config) {
finalizeConfig(proto, config);
let props = ownPropertiesForClass(klass);
if (props) {
finalizeProperties(proto, props);
}
let observers = ownObserversForClass(klass);
if (observers) {
finalizeObservers(proto, observers, props);
}
let template = klass.template;
if (template) {
@@ -156,26 +176,30 @@ Polymer.ElementMixin = Polymer.dedupingMixin(function(base) {
}
/**
* Configures a `proto` based on a `config` object.
* Configures a `proto` based on a `properties` object.
* Leverages `PropertyEffects` to create property accessors and effects
* supporting, observers, reflecting to attributes, change notification,
* computed properties, and read only properties.
* @param {HTMLElement} proto
* @param {Object} config
* @param {Object} properties
* @private
*/
function finalizeConfig(proto, config) {
let {properties, observers} = config;
if (properties) {
// process properties
for (let p in properties) {
createPropertyFromConfig(proto, p, properties[p], properties);
}
function finalizeProperties(proto, properties) {
for (let p in properties) {
createPropertyFromConfig(proto, p, properties[p], properties);
}
if (observers) {
for (let i=0; i < observers.length; i++) {
proto._createMethodObserver(observers[i], properties);
}
}
/**
* Configures a `proto` based on a `observers` array.
* Leverages `PropertyEffects` to create observers.
* @param {HTMLElement} proto
* @param {Array} observers
* @private
*/
function finalizeObservers(proto, observers, dynamicProperties) {
for (let i=0; i < observers.length; i++) {
proto._createMethodObserver(observers[i], dynamicProperties);
}
}

View File

@@ -34,10 +34,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
HTMLImports.whenReady(function() {
class BaseEl extends Polymer.Element {
static get is() { return 'base-el' }
static get config() {
return {
properties: { foo: { type: String, value: 5 } }
}
static get properties() {
return { foo: { type: String, value: 5 } }
}
}
customElements.define(BaseEl.is, BaseEl);
@@ -51,10 +49,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
HTMLImports.whenReady(function() {
class ChildEl extends window.BaseEl {
static get is() { return 'child-el' }
static get config() {
return {
properties: { bar: { type: String, value: 3 } }
}
static get properties() {
return { bar: { type: String, value: 3 } }
}
}
customElements.define(ChildEl.is, ChildEl);
@@ -68,10 +64,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
HTMLImports.whenReady(function() {
class GrandChildEl extends window.ChildEl {
static get is() { return 'grand-child-el' }
static get config() {
return {
properties: { bar: { type: String, value: 3 } }
}
static get properties() {
return { bar: { type: String, value: 3 } }
}
}
customElements.define(GrandChildEl.is, GrandChildEl);
@@ -86,10 +80,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
HTMLImports.whenReady(function() {
class ChildElWithTemplate extends window.GrandChildEl {
static get is() { return 'child-el-with-template' }
static get config() {
return {
properties: { bar: { type: String, value: 3 } }
}
static get properties() {
return { bar: { type: String, value: 3 } }
}
static get template() {
var template = window.GrandChildEl.template.cloneNode(true);

View File

@@ -36,15 +36,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</dom-module>
<script>
HTMLImports.whenReady(() => {
class XMixin extends Polymer.LegacyElement {
class XMixin extends Polymer.LegacyElementMixin(HTMLElement) {
static get is() { return 'x-mixin' }
static get config() {
static get properties() {
return {
properties: {
stream: {
value: () => [],
type: Array
}
stream: {
value: () => [],
type: Array
}
}
}

View File

@@ -224,25 +224,17 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
class extends Polymer.mixinBehaviors(
[Polymer.BehaviorA, Polymer.BehaviorB], Polymer.Element) {
static get config() {
static get properties() {
return {
hostAttributes: {
element: 'element'
foo: {
type: String,
reflectToAttribute: true,
readOnly: true,
observer: '_fooChanged'
},
properties: {
foo: {
type: String,
reflectToAttribute: true,
readOnly: true,
observer: '_fooChanged'
},
overridableProperty: {
value: true
}
overridableProperty: {
value: true
}
}
}

View File

@@ -27,26 +27,27 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
static get is() { return 'my-element'; }
static get config() {
static get properties() {
return {
properties: {
prop: {
value: 'base',
observer: '_propObserver'
},
computedPropDep: {
value: true
},
computedProp: {
computed: '_compute(computedPropDep)'
}
prop: {
value: 'base',
observer: '_propObserver'
},
observers: [
'_methodObserver(prop)'
]
computedPropDep: {
value: true
},
computedProp: {
computed: '_compute(computedPropDep)'
}
}
}
static get observers() {
return [
'_methodObserver(prop)'
];
}
constructor() {
super();
this._propObserver = sinon.spy();
@@ -99,26 +100,27 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
static get is() { return 'sub-element'; }
static get config() {
static get properties() {
return {
properties: {
prop: {
value: 'sub'
},
prop2: {
value: 'prop2',
observer: '_prop2Observer'
},
computedPropDep: {
computed: '_compute(prop2)'
}
prop: {
value: 'sub'
},
observers: [
'_subMethodObserver(prop, prop2)'
]
prop2: {
value: 'prop2',
observer: '_prop2Observer'
},
computedPropDep: {
computed: '_compute(prop2)'
}
}
}
static get observers() {
return [
'_subMethodObserver(prop, prop2)'
];
}
constructor() {
super();
this._calledConstructor++;
@@ -154,23 +156,24 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
function MyMixin(Base) {
return class extends Base {
static get config() {
static get properties() {
return {
properties: {
prop: {
value: 'mixin'
},
mixin: {
value: 'mixin',
observer: '_mixinObserver'
}
prop: {
value: 'mixin'
},
observers: [
'_mixinMethodObserver(mixin, prop, prop2)'
]
mixin: {
value: 'mixin',
observer: '_mixinObserver'
}
}
}
static get observers() {
return [
'_mixinMethodObserver(mixin, prop, prop2)'
]
}
constructor() {
super();
this._calledConstructor++;
@@ -197,20 +200,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
static get is() { return 'sub-mixin-element'; }
static get config() {
static get properties() {
return {
properties: {
prop3: {
value: 'prop3',
observer: '_prop3Observer'
}
},
observers: [
'_subMixinMethodObserver(mixin, prop, prop2, prop3)'
]
prop3: {
value: 'prop3',
observer: '_prop3Observer'
}
}
}
static get observers() {
return [
'_subMixinMethodObserver(mixin, prop, prop2, prop3)'
]
}
constructor() {
super();
this._calledConstructor++;
@@ -250,12 +254,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
class SubNewTemplate extends window.MyElement {
static get is() { return 'sub-new-template'; }
static get config() {
static get properties() {
return {
properties: {
prop2: {
value: 'prop2',
}
prop2: {
value: 'prop2',
}
}
}

View File

@@ -23,16 +23,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<script>
HTMLImports.whenReady(function() {
class MyElement extends Polymer.LegacyElement {
class MyElement extends Polymer.LegacyElementMixin(HTMLElement) {
static get is() { return 'my-element'; }
static get config() {
static get properties() {
return {
properties: {
prop: {
value: 'base'
}
prop: {
value: 'base'
}
}
}
@@ -111,7 +109,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</test-fixture>
<script>
suite('class extends Polymer.LegacyElement', function() {
suite('class extends Polymer.LegacyElementMixin(HTMLElement)', function() {
var el;
@@ -127,7 +125,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
test('instanceof', function() {
assert.instanceOf(el, HTMLElement);
assert.instanceOf(el, Polymer.Element);
assert.instanceOf(el, Polymer.LegacyElement);
assert.instanceOf(el, window.MyElement);
});
@@ -162,7 +159,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
test('instanceof', function() {
assert.instanceOf(el, HTMLElement);
assert.instanceOf(el, Polymer.Element);
assert.instanceOf(el, Polymer.LegacyElement);
assert.instanceOf(el, window.MyElement);
assert.instanceOf(el, window.SubElement);
});
@@ -185,7 +181,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
});
test('Polymer.LegacyElementMixin caches', function() {
assert.equal(Polymer.LegacyElement, Polymer.LegacyElementMixin(HTMLElement));
assert.equal(Polymer.LegacyElementMixin(HTMLElement),
Polymer.LegacyElementMixin(HTMLElement));
});

View File

@@ -18,7 +18,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<script>
HTMLImports.whenReady(function() {
class XFoo extends Polymer.LegacyElement {
class XFoo extends Polymer.LegacyElementMixin(HTMLElement) {
ready() {
super.ready();
Polymer.RenderStatus.beforeNextRender(this, this.beforeNextRender,