UNPKG

infamous

Version:

A CSS3D/WebGL UI library.

112 lines (93 loc) 4.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _lowclass = _interopRequireDefault(require("lowclass")); var _Mixin = _interopRequireDefault(require("../../core/Mixin")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _default = (0, _Mixin.default)(Base => // TODO This is here for now. Make it an extension to // element-behaviors so that it can be applied to any element // generically. (0, _lowclass.default)('DefaultBehaviors').extends(Base, ({ Super }) => ({ static: { // override in subclasses defaultBehaviors: [] }, constructor(...args) { // Use the constructor return value, because we assume Base must be // a HTML element class, in which case this is currently unavoidable // because lowclass currently uses ES5-style constructors. This may // change when lowclass has `class` syntax option. const self = Super(this).constructor(...args); // If no geometry or material behavior is detected, add default ones. // // Deferring to a microtask doesn't work here, we must defer to a // macrotask with setTimeout, so we can detect if the element has // initial behaviors, otherwise the element's initial attributes // haven't been added yet (this is how HTML engines work, see // https://github.com/whatwg/dom/issues/522). // // TODO: If we use setTimeout (macrotask) deferral anywhere (like we do // here), and maybe even with microtask deferral (f.e. Promise), maybe // we should have a single place that initiate this deferral so that // everything in the engine can hook into it. Otherwise if different // call sites use setTimeout, logic will be firing at random and in // different order. setTimeout(() => self._setDefaultBehaviorsIfNeeded(), 0); return self; }, _setDefaultBehaviorsIfNeeded() { let defaultBehaviors = this.constructor.defaultBehaviors; // do nothing if there's no defaults if (!defaultBehaviors) return; if (Object.keys(defaultBehaviors).length == 0) return; const initialBehaviorNames = Array.from(this.behaviors.keys()); // small optimization: if there are no initial behaviors and we // have default behaviors, just set the default behaviors. if (initialBehaviorNames.length == 0) { // if not an array, then it's an object. if (!(defaultBehaviors instanceof Array)) defaultBehaviors = Object.keys(defaultBehaviors); this.setAttribute('has', this.getAttribute('has') + ` ${defaultBehaviors.join(' ')}`); } // otherwise detect which default behavior(s) to add else { let behaviorNamesToAdd = ''; // if defaultBehaviors is an array, use default logic to add // behaviors that aren't already added. if (defaultBehaviors instanceof Array) { for (const defaultBehaviorName of defaultBehaviors) { let hasBehavior = false; for (const initialBehaviorName of initialBehaviorNames) { if (defaultBehaviorName == initialBehaviorName) { hasBehavior = true; break; } } if (hasBehavior) continue;else { // TODO programmatic API: //this.behaviors.add('box-geometry') // add a space in front of each name except the first if (behaviorNamesToAdd) behaviorNamesToAdd += ' '; behaviorNamesToAdd += defaultBehaviorName; } } } // if defaultBehaviors is an object, then behaviors are added // based on conditions. else if (typeof defaultBehaviors == 'object') { const defaultBehaviorNames = Object.keys(defaultBehaviors); for (const defaultBehaviorName of defaultBehaviorNames) { const condition = defaultBehaviors[defaultBehaviorName]; if (typeof condition == 'function' && condition(initialBehaviorNames) || typeof condition != 'function' && condition) { // add a space in front of each name except the first if (behaviorNamesToAdd) behaviorNamesToAdd += ' '; behaviorNamesToAdd += defaultBehaviorName; } } } // add the needed behaviors all at once. if (behaviorNamesToAdd) { let currentHasValue = this.getAttribute('has'); if (currentHasValue) currentHasValue += ' '; this.setAttribute('has', currentHasValue + behaviorNamesToAdd); } } } }))); exports.default = _default;