UNPKG

foam-framework

Version:
204 lines (177 loc) 5.77 kB
/** * @license * Copyright 2012 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ CLASS({ package: 'foam.ui', name: 'PopupChoiceView', extends: 'foam.ui.AbstractChoiceView', requires: ['foam.ui.ChoiceListView'], traits: ['foam.input.touch.VerticalScrollNativeTrait'], properties: [ { name: 'linkLabel' }, { name: 'iconUrl' }, { name: 'tagName', defaultValue: 'span' }, { name: 'className', defaultValue: 'popupChoiceView' }, { model_: 'BooleanProperty', name: 'showValue' }, { model_: 'BooleanProperty', name: 'opened', transient: true }, { model_: 'FunctionProperty', name: 'updateListener' }, { name: 'mode', defaultValue: 'read-write' }, { name: 'scrollerID', factory: function() { return this.id + '-popup-list-scroller'; } } ], actions: [ { name: 'open', labelFn: function() { return this.linkLabel; }, code: function() { if ( this.opened ) return; var self = this; var view = this.ChoiceListView.create({ id: this.scrollerID, className: 'popupChoiceList', data: this.data, choices: this.choices, autoSetData: this.autoSetData }); self.opened = true; var pos = findViewportXY(this.$.querySelector('.action')); var e = this.X.document.body.insertAdjacentHTML('beforeend', view.toHTML()); var s = this.X.window.getComputedStyle(view.$); function mouseMove(evt) { // Containment is not sufficient. // It makes the popup too eager to close since the mouse can start // slightly outside the box. We need to check the coordinates, and // only close it when it's not upwards and leftwards of the box edges, // ie. to pretend the popup reaches the top and right of the window. if ( view.$.contains(evt.target) ) return; var margin = 50; var left = view.$.offsetLeft - margin; var right = view.$.offsetWidth + left + 2*margin; var top = view.$.offsetTop - margin; var bottom = view.$.offsetHeight + top + 2*margin; if ( (left < evt.pageX && evt.pageX < right) && (top < evt.pageY && evt.pageY < bottom) ) return; remove(); } var removeListener; function remove() { self.opened = false; self.X.document.removeEventListener('touchstart', removeListener); self.X.document.removeEventListener('mousemove', mouseMove); if ( view.$ ) view.$.outerHTML = ''; } removeListener = function(evt) { if (view && view.$ && view.$.contains(evt.target)) return; remove(); }; // I don't know why the 'animate' is required, but it sometimes // doesn't remove the view without it. view.data$.addListener(EventService.framed(function() { self.data = view.data; remove(); }, this.X)); view.$.style.top = (pos[1]-2) + 'px'; var left = Math.max(0, pos[0] - toNum(s.width) + 30); view.$.style.left = left + 'px'; view.$.style.maxHeight = (Math.max(200, this.X.window.innerHeight-pos[1]-10)) + 'px'; view.initHTML(); this.X.document.addEventListener('touchstart', removeListener); this.X.document.addEventListener('mousemove', mouseMove); } } ], methods: { toHTML: function() { if ( this.mode === 'read-only' ) { return '<span id="' + this.id + '" class="popupChoiceView-readonly">' + ((this.choice && this.choice[1]) || '') + '</span>'; } return this.SUPER(); }, toInnerHTML: function() { if ( this.mode === 'read-only' ) { return (this.choice && this.choice[1]) || ''; } var out = ''; if ( this.showValue ) { var id = this.nextID(); out += '<span id="' + id + '" class="value">' + ((this.choice && this.choice[1]) || '') + '</span>'; // Remove any previous data$ listener for this popup. if ( this.updateListener ) this.data$.removeListener(this.updateListener); this.updateListener = function() { var e = this.X.$(id); if ( e ) e.innerHTML = this.choice[1]; }.bind(this); this.data$.addListener(this.updateListener); } out += '<span class="action">'; var action = this.model_.getAction('open'); action.iconUrl = this.iconUrl; var button = this.createActionView(action).toView_(); this.addSelfDataChild(button); out += button.toHTML(); out += '</span>'; return out; } }, templates: [ function CSS() {/* .popupChoiceList { border: 2px solid grey; background: white; display: table-footer-group; overflow-y: auto; position: absolute; top: 20; left: 50; margin: 0; } .popupChoiceList li { display: block; margin: 15px; margin-left: -20px; } */} ] });