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 * Adds separator to drop-down list. |
69 enlarge(this.popup_, this); | 39 */ |
| 40 addSeparator: function() { |
| 41 this.menu.addSeparator(); |
| 42 }, |
| 43 |
| 44 /** |
| 45 * Default item to fire on combobox click |
| 46 */ |
| 47 get defaultItem() { |
| 48 return this.defaultItem_; |
| 49 }, |
| 50 set defaultItem(defaultItem) { |
| 51 this.defaultItem_ = defaultItem; |
| 52 if (defaultItem.label) { |
| 53 this.labelNode_.textContent = defaultItem.label; |
| 54 } else { |
| 55 this.labelNode_.textContent = ''; |
| 56 } |
| 57 |
| 58 if (defaultItem.iconUrl) { |
| 59 this.iconNode_.src = defaultItem.iconUrl; |
| 60 } else { |
| 61 this.iconNode_.src = ''; |
| 62 } |
70 }, | 63 }, |
71 | 64 |
72 /** | 65 /** |
73 * Initializes the element. | 66 * Initializes the element. |
74 */ | 67 */ |
75 decorate: function() { | 68 decorate: function() { |
76 this.items_ = []; | 69 cr.ui.MenuButton.prototype.decorate.call(this); |
77 | 70 |
78 this.classList.add('combobutton'); | 71 this.classList.add('combobutton'); |
79 | 72 |
| 73 this.iconNode_ = this.ownerDocument.createElement('img'); |
| 74 this.appendChild(this.iconNode_); |
| 75 |
| 76 this.labelNode_ = this.ownerDocument.createElement('span'); |
| 77 this.appendChild(this.labelNode_); |
| 78 |
80 var triggerIcon = this.ownerDocument.createElement('span'); | 79 var triggerIcon = this.ownerDocument.createElement('span'); |
81 triggerIcon.className = 'disclosureindicator'; | 80 triggerIcon.className = 'disclosureindicator'; |
82 this.trigger_ = this.ownerDocument.createElement('div'); | 81 this.trigger_ = this.ownerDocument.createElement('div'); |
83 this.trigger_.appendChild(triggerIcon); | 82 this.trigger_.appendChild(triggerIcon); |
84 | 83 |
85 this.popup_ = this.ownerDocument.createElement('div'); | 84 this.appendChild(this.trigger_); |
86 this.popup_.className = 'popup'; | |
87 | 85 |
88 this.appendChild(this.trigger_); | 86 this.addEventListener('click', this.handleButtonClick_.bind(this)); |
89 this.appendChild(this.popup_); | |
90 | 87 |
91 this.addEventListener('click', | |
92 this.handleButtonClick_.bind(this)); | |
93 this.popup_.addEventListener('click', | |
94 this.handlePopupClick_.bind(this)); | |
95 this.trigger_.addEventListener('click', | 88 this.trigger_.addEventListener('click', |
96 this.handleTriggerClicked_.bind(this)); | 89 this.handleTriggerClicked_.bind(this)); |
97 this.addEventListener('mouseout', this.handleMouseOut_.bind(this)); | |
98 | 90 |
99 this.visible = true; | 91 this.menu.addEventListener('activate', |
| 92 this.handleMenuActivate_.bind(this)); |
| 93 |
| 94 // Remove mousedown event listener created by MenuButton::decorate, |
| 95 // and move it down to trigger_. |
| 96 this.removeEventListener('mousedown', this); |
| 97 this.trigger_.addEventListener('mousedown', this); |
| 98 }, |
| 99 |
| 100 /** |
| 101 * Handles the keydown event for the menu button. |
| 102 */ |
| 103 handleKeyDown: function(e) { |
| 104 switch (e.keyIdentifier) { |
| 105 case 'Down': |
| 106 case 'Up': |
| 107 if (!this.isMenuShown()) |
| 108 this.showMenu(); |
| 109 e.preventDefault(); |
| 110 break; |
| 111 case 'Esc': |
| 112 case 'U+001B': // Maybe this is remote desktop playing a prank? |
| 113 this.hideMenu(); |
| 114 break; |
| 115 } |
100 }, | 116 }, |
101 | 117 |
102 handleTriggerClicked_: function(event) { | 118 handleTriggerClicked_: function(event) { |
103 this.open = !this.open; | |
104 event.stopPropagation(); | 119 event.stopPropagation(); |
105 }, | 120 }, |
106 | 121 |
107 handleMouseOut_: function(event) { | 122 handleMenuActivate_: function(event) { |
108 var x = event.x; | 123 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 }, | 124 }, |
121 | 125 |
122 handleButtonClick_: function(event) { | 126 handleButtonClick_: function() { |
123 this.dispatchSelectEvent(this.items_[0]); | 127 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 }, | 128 }, |
137 | 129 |
138 dispatchSelectEvent: function(item) { | 130 dispatchSelectEvent: function(item) { |
139 var selectEvent = new Event('select'); | 131 var selectEvent = new Event('select'); |
140 selectEvent.item = item; | 132 selectEvent.item = item; |
141 this.dispatchEvent(selectEvent); | 133 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 } | 134 } |
155 }; | 135 }; |
156 | 136 |
157 cr.defineProperty(ComboButton, 'disabled', cr.PropertyKind.BOOL_ATTR); | 137 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); | 138 cr.defineProperty(ComboButton, 'multiple', cr.PropertyKind.BOOL_ATTR); |
160 | 139 |
161 return { | 140 return { |
162 ComboButton: ComboButton | 141 ComboButton: ComboButton |
163 }; | 142 }; |
164 }); | 143 }); |
OLD | NEW |