OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview This implements a combobutton control. | 6 * @fileoverview This implements a combobutton control. |
7 */ | 7 */ |
8 | 8 |
9 cr.define('cr.ui', function() { | 9 cr.define('cr.ui', function() { |
10 /** | 10 /** |
11 * Sets minWidth for the target, so it's visually as large as source. | |
12 * @param {HTMLElement} target Element which min-width to tune. | |
13 * @param {HTMLElement} source Element, which width to use. | |
14 */ | |
15 function enlarge(target, source) { | |
16 var cs = target.ownerDocument.defaultView.getComputedStyle(target); | |
17 target.style.minWidth = (source.getBoundingClientRect().width - | |
18 parseFloat(cs.borderLeftWidth) - | |
19 parseFloat(cs.borderRightWidth)) + 'px'; | |
20 } | |
21 | |
22 /** | |
23 * Creates a new combobutton element. | 11 * Creates a new combobutton element. |
24 * @param {Object=} opt_propertyBag Optional properties. | 12 * @param {Object=} opt_propertyBag Optional properties. |
25 * @constructor | 13 * @constructor |
26 * @extends {HTMLUListElement} | 14 * @extends {HTMLUListElement} |
27 */ | 15 */ |
28 var ComboButton = cr.ui.define('div'); | 16 var ComboButton = cr.ui.define(cr.ui.MenuButton); |
| 17 |
29 | 18 |
30 ComboButton.prototype = { | 19 ComboButton.prototype = { |
31 __proto__: HTMLDivElement.prototype, | 20 __proto__: cr.ui.MenuButton.prototype, |
| 21 |
| 22 defaultItem_: null, |
32 | 23 |
33 /** | 24 /** |
34 * The items list. | 25 * Truncates drop-down list. |
35 */ | 26 */ |
36 items_: null, | |
37 | |
38 clear: function() { | 27 clear: function() { |
39 if (this.items_.length > 0) | 28 this.menu.clear(); |
40 // Remove default combobox item if we have added it at addItem. | |
41 this.removeChild(this.firstChild); | |
42 | |
43 this.items_ = []; | |
44 this.popup_.textContent = ''; | |
45 this.multiple = false; | 29 this.multiple = false; |
46 this.popup_.style.minWidth = ''; | |
47 }, | 30 }, |
48 | 31 |
49 addItem: function(item) { | 32 addDropDownItem: function(item) { |
50 this.items_.push(item); | 33 this.multiple = true; |
51 if (this.items_.length == 1) { | 34 this.menu.addMenuItem(item).data = item; |
52 // Set first added item as default on combobox. | |
53 // First item should be the first element to prepend drop-down arrow and | |
54 // popup layer. | |
55 this.insertBefore(item, this.firstChild); | |
56 } else { | |
57 this.multiple = true; | |
58 if (this.popup_.hasChildNodes()) | |
59 this.popup_.insertBefore(item, this.popup_.firstChild); | |
60 else | |
61 this.popup_.appendChild(item); | |
62 if (this.visible) | |
63 this.setPopupSize_(); | |
64 } | |
65 }, | 35 }, |
66 | 36 |
67 setPopupSize_: function() { | 37 /** |
68 this.popup_.style.bottom = (this.clientHeight + 1) + 'px'; | 38 * Default item to fire on combobox click |
69 enlarge(this.popup_, this); | 39 */ |
| 40 get defaultItem() { |
| 41 return this.defaultItem_; |
| 42 }, |
| 43 set defaultItem(defaultItem) { |
| 44 this.defaultItem_ = defaultItem; |
| 45 if (defaultItem.label) { |
| 46 this.labelNode_.textContent = defaultItem.label; |
| 47 } else { |
| 48 this.labelNode_.textContent = ''; |
| 49 } |
| 50 |
| 51 if (defaultItem.iconUrl) { |
| 52 this.iconNode_.src = defaultItem.iconUrl; |
| 53 } else { |
| 54 this.iconNode_.src = ''; |
| 55 } |
70 }, | 56 }, |
71 | 57 |
72 /** | 58 /** |
73 * Initializes the element. | 59 * Initializes the element. |
74 */ | 60 */ |
75 decorate: function() { | 61 decorate: function() { |
76 this.items_ = []; | 62 cr.ui.MenuButton.prototype.decorate.call(this); |
77 | 63 |
78 this.classList.add('combobutton'); | 64 this.classList.add('combobutton'); |
79 | 65 |
| 66 this.iconNode_ = this.ownerDocument.createElement('img'); |
| 67 this.appendChild(this.iconNode_); |
| 68 |
| 69 this.labelNode_ = this.ownerDocument.createElement('span'); |
| 70 this.appendChild(this.labelNode_); |
| 71 |
80 var triggerIcon = this.ownerDocument.createElement('span'); | 72 var triggerIcon = this.ownerDocument.createElement('span'); |
81 triggerIcon.className = 'disclosureindicator'; | 73 triggerIcon.className = 'disclosureindicator'; |
82 this.trigger_ = this.ownerDocument.createElement('div'); | 74 this.trigger_ = this.ownerDocument.createElement('div'); |
83 this.trigger_.appendChild(triggerIcon); | 75 this.trigger_.appendChild(triggerIcon); |
84 | 76 |
85 this.popup_ = this.ownerDocument.createElement('div'); | 77 this.appendChild(this.trigger_); |
86 this.popup_.className = 'popup'; | |
87 | 78 |
88 this.appendChild(this.trigger_); | 79 this.addEventListener('click', this.handleButtonClick_.bind(this)); |
89 this.appendChild(this.popup_); | |
90 | 80 |
91 this.addEventListener('click', | |
92 this.handleButtonClick_.bind(this)); | |
93 this.popup_.addEventListener('click', | |
94 this.handlePopupClick_.bind(this)); | |
95 this.trigger_.addEventListener('click', | 81 this.trigger_.addEventListener('click', |
96 this.handleTriggerClicked_.bind(this)); | 82 this.handleTriggerClicked_.bind(this)); |
97 this.addEventListener('mouseout', this.handleMouseOut_.bind(this)); | |
98 | 83 |
99 this.visible = true; | 84 this.menu.addEventListener('activate', |
| 85 this.handleMenuActivate_.bind(this)); |
| 86 |
| 87 // Remove mousedown event listener created by MenuButton::decorate, |
| 88 // and move it down to trigger_. |
| 89 this.removeEventListener('mousedown', this); |
| 90 this.trigger_.addEventListener('mousedown', this); |
| 91 }, |
| 92 |
| 93 /** |
| 94 * Handles the keydown event for the menu button. |
| 95 */ |
| 96 handleKeyDown: function(e) { |
| 97 switch (e.keyIdentifier) { |
| 98 case 'Down': |
| 99 case 'Up': |
| 100 if (!this.isMenuShown()) |
| 101 this.showMenu(); |
| 102 e.preventDefault(); |
| 103 break; |
| 104 case 'Esc': |
| 105 case 'U+001B': // Maybe this is remote desktop playing a prank? |
| 106 this.hideMenu(); |
| 107 break; |
| 108 } |
100 }, | 109 }, |
101 | 110 |
102 handleTriggerClicked_: function(event) { | 111 handleTriggerClicked_: function(event) { |
103 this.open = !this.open; | |
104 event.stopPropagation(); | 112 event.stopPropagation(); |
105 }, | 113 }, |
106 | 114 |
107 handleMouseOut_: function(event) { | 115 handleMenuActivate_: function(event) { |
108 var x = event.x; | 116 this.dispatchSelectEvent(event.target.data); |
109 var y = event.y; | |
110 | |
111 var children = this.childNodes; | |
112 for (var i = 0; i < children.length; i++) | |
113 { | |
114 var r = this.children[i].getBoundingClientRect(); | |
115 if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) | |
116 return; | |
117 } | |
118 | |
119 this.open = false; | |
120 }, | 117 }, |
121 | 118 |
122 handleButtonClick_: function(event) { | 119 handleButtonClick_: function() { |
123 this.dispatchSelectEvent(this.items_[0]); | 120 this.dispatchSelectEvent(this.defaultItem_); |
124 }, | |
125 | |
126 handlePopupClick_: function(event) { | |
127 var item = event.target; | |
128 while (item && item.parentNode != this.popup_) | |
129 item = item.parentNode; | |
130 if (!item) | |
131 return; | |
132 | |
133 this.open = false; | |
134 this.dispatchSelectEvent(item); | |
135 event.stopPropagation(); | |
136 }, | 121 }, |
137 | 122 |
138 dispatchSelectEvent: function(item) { | 123 dispatchSelectEvent: function(item) { |
139 var selectEvent = new Event('select'); | 124 var selectEvent = new Event('select'); |
140 selectEvent.item = item; | 125 selectEvent.item = item; |
141 this.dispatchEvent(selectEvent); | 126 this.dispatchEvent(selectEvent); |
142 }, | |
143 | |
144 get visible() { | |
145 return this.hasAttribute('visible'); | |
146 }, | |
147 set visible(value) { | |
148 if (value) { | |
149 this.setAttribute('visible', 'visible'); | |
150 setTimeout(this.setPopupSize_.bind(this), 0); | |
151 } else { | |
152 this.removeAttribute('visible'); | |
153 } | |
154 } | 127 } |
155 }; | 128 }; |
156 | 129 |
157 cr.defineProperty(ComboButton, 'disabled', cr.PropertyKind.BOOL_ATTR); | 130 cr.defineProperty(ComboButton, 'disabled', cr.PropertyKind.BOOL_ATTR); |
158 cr.defineProperty(ComboButton, 'open', cr.PropertyKind.BOOL_ATTR); | |
159 cr.defineProperty(ComboButton, 'multiple', cr.PropertyKind.BOOL_ATTR); | 131 cr.defineProperty(ComboButton, 'multiple', cr.PropertyKind.BOOL_ATTR); |
160 | 132 |
161 return { | 133 return { |
162 ComboButton: ComboButton | 134 ComboButton: ComboButton |
163 }; | 135 }; |
164 }); | 136 }); |
OLD | NEW |