VulpesOS/gaia/tv_apps/smart-home/bower_components/menu-group/script.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

128 lines
4.2 KiB
JavaScript
Executable file

'use strict';
/* global evt */
window.MenuGroup = (function(win) {
// Extend from the HTMLElement prototype
var proto = evt(Object.create(HTMLElement.prototype));
proto.createdCallback = function() {
this.style.width = '0';
this.classList.add('closed');
this.addEventListener('transitionend', this);
};
proto.calculateChildWidth = function() {
var child = this.firstElementChild;
var childWidth = 0;
var style;
while(child) {
style = window.getComputedStyle(child);
childWidth += child.offsetWidth + parseInt(style.marginLeft, 10) +
parseInt(style.marginRight, 10);
child = child.nextElementSibling;
}
return childWidth;
};
proto.fireEvent = function(event, detail) {
var evtObject = new CustomEvent(event, {
bubbles: false,
detail: detail || this
});
this.dispatchEvent(evtObject);
this.fire(event, detail);
};
proto.handleEvent = function(evt) {
switch(evt.type) {
// Like System app, the transition is our state machine.
case 'transitionend':
// We only process 'background-color' because all states have this
// change.
if ((evt.propertyName !== 'background-color' &&
evt.propertyName !== 'width') ||
evt.target !== this) {
break;
}
if (this.classList.contains('enlarging')) {
this.classList.remove('enlarging');
this.classList.add('shrinking');
// change to shrinking
} else if (this.classList.contains('shrinking')) {
// XXX: this is a workaround of CSS transform. We cannot have a
// rotation right after resizing without any settimeout???
setTimeout((function() {
if (!this.classList.contains('shrinking')) {
// If we don't have shrinking class here, that means this group
// is changing to another state and we don't need to opening
// state.
return;
}
this.classList.remove('shrinking');
this.classList.remove('closed');
this.classList.add('opening');
// change to opening
this.style.width = this.calculateChildWidth() + 'px';
}).bind(this));
} else if (this.classList.contains('opening')) {
this.classList.remove('opening');
// final state: opened
this.fireEvent('opened');
} else if (this.classList.contains('closing')) {
this.classList.remove('closing');
this.classList.add('closed');
// final state: closed
this.fireEvent('closed');
}
break;
}
};
proto.focus = proto.open = function() {
this.fireEvent('will-open');
// If we get focus when we closing the group, we need to cancel the closing
// state.
this.classList.remove('shrinking');
this.classList.remove('opening');
this.classList.remove('closing');
this.classList.add('enlarging');
};
proto.blur = proto.close = function() {
this.fireEvent('will-close');
// We may call close at mid state, we need to reset all of them and go to
// closing state
this.classList.remove('enlarging');
this.classList.remove('shrinking');
this.classList.remove('opening');
this.classList.add('closing');
this.style.width = '0';
};
proto.changeIcon = function(icon) {
if (!this.dataset.icon) {
this.dataset.icon = icon;
return;
}
var current = this.dataset.icon.split(' ')[0];
if (current === icon) {
return;
}
this.classList.add('switching-icon');
this.dataset.icon = icon + ' ' + current;
// Let gecko to recalculate the value and remove it.
// We shouldn't put any value at the setTimeout, but it doesn't work in some
// device, like FirefoxNightly, b2g-desktop. The 100 ms works well in
// these environments.
var self = this;
setTimeout(function() {
self.classList.remove('switching-icon');
}, 100);
};
// Register and return the constructor
return document.registerElement('menu-group', { prototype: proto });
})(window);