TreePicker.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /**
  2. * @class Ext.ux.TreePicker
  3. * @extends Ext.form.field.Picker
  4. *
  5. * A Picker field that contains a tree panel on its popup, enabling selection of tree nodes.
  6. */
  7. Ext.define('Ext.ux.TreePicker', {
  8. extend: 'Ext.form.field.Picker',
  9. xtype: 'treepicker',
  10. triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',
  11. config: {
  12. /**
  13. * @cfg {Ext.data.TreeStore} store
  14. * A tree store that the tree picker will be bound to
  15. */
  16. store: null,
  17. /**
  18. * @cfg {String} displayField
  19. * The field inside the model that will be used as the node's text.
  20. * Defaults to the default value of {@link Ext.tree.Panel}'s `displayField` configuration.
  21. */
  22. displayField: null,
  23. /**
  24. * @cfg {Array} columns
  25. * An optional array of columns for multi-column trees
  26. */
  27. columns: null,
  28. /**
  29. * @cfg {Boolean} selectOnTab
  30. * Whether the Tab key should select the currently highlighted item. Defaults to `true`.
  31. */
  32. selectOnTab: true,
  33. /**
  34. * @cfg {Number} maxPickerHeight
  35. * The maximum height of the tree dropdown. Defaults to 300.
  36. */
  37. maxPickerHeight: 300,
  38. /**
  39. * @cfg {Number} minPickerHeight
  40. * The minimum height of the tree dropdown. Defaults to 100.
  41. */
  42. minPickerHeight: 100
  43. },
  44. editable: false,
  45. initComponent: function() {
  46. var me = this;
  47. me.callParent(arguments);
  48. this.addEvents(
  49. /**
  50. * @event select
  51. * Fires when a tree node is selected
  52. * @param {Ext.ux.TreePicker} picker This tree picker
  53. * @param {Ext.data.Model} record The selected record
  54. */
  55. 'select'
  56. );
  57. me.store.on('load', me.onLoad, me);
  58. },
  59. /**
  60. * Creates and returns the tree panel to be used as this field's picker.
  61. * @private
  62. */
  63. createPicker: function() {
  64. var me = this,
  65. picker = Ext.create('Ext.tree.Panel', {
  66. store: me.store,
  67. floating: true,
  68. hidden: true,
  69. displayField: me.displayField,
  70. columns: me.columns,
  71. maxHeight: me.maxTreeHeight,
  72. shadow: false,
  73. manageHeight: false,
  74. listeners: {
  75. itemclick: Ext.bind(me.onItemClick, me)
  76. },
  77. viewConfig: {
  78. listeners: {
  79. render: function(view) {
  80. view.getEl().on('keypress', me.onPickerKeypress, me);
  81. }
  82. }
  83. }
  84. }),
  85. view = picker.getView();
  86. view.on('render', me.setPickerViewStyles, me);
  87. if (Ext.isIE9 && Ext.isStrict) {
  88. // In IE9 strict mode, the tree view grows by the height of the horizontal scroll bar when the items are highlighted or unhighlighted.
  89. // Also when items are collapsed or expanded the height of the view is off. Forcing a repaint fixes the problem.
  90. view.on('highlightitem', me.repaintPickerView, me);
  91. view.on('unhighlightitem', me.repaintPickerView, me);
  92. view.on('afteritemexpand', me.repaintPickerView, me);
  93. view.on('afteritemcollapse', me.repaintPickerView, me);
  94. }
  95. return picker;
  96. },
  97. /**
  98. * Sets min/max height styles on the tree picker's view element after it is rendered.
  99. * @param {Ext.tree.View} view
  100. * @private
  101. */
  102. setPickerViewStyles: function(view) {
  103. view.getEl().setStyle({
  104. 'min-height': this.minPickerHeight + 'px',
  105. 'max-height': this.maxPickerHeight + 'px'
  106. });
  107. },
  108. /**
  109. * repaints the tree view
  110. */
  111. repaintPickerView: function() {
  112. var style = this.picker.getView().getEl().dom.style;
  113. // can't use Element.repaint because it contains a setTimeout, which results in a flicker effect
  114. style.display = style.display;
  115. },
  116. /**
  117. * Aligns the picker to the input element
  118. * @private
  119. */
  120. alignPicker: function() {
  121. var me = this,
  122. picker;
  123. if (me.isExpanded) {
  124. picker = me.getPicker();
  125. if (me.matchFieldWidth) {
  126. // Auto the height (it will be constrained by max height)
  127. picker.setWidth(me.bodyEl.getWidth());
  128. }
  129. if (picker.isFloating()) {
  130. me.doAlign();
  131. }
  132. }
  133. },
  134. /**
  135. * Handles a click even on a tree node
  136. * @private
  137. * @param {Ext.tree.View} view
  138. * @param {Ext.data.Model} record
  139. * @param {HTMLElement} node
  140. * @param {Number} rowIndex
  141. * @param {Ext.EventObject} e
  142. */
  143. onItemClick: function(view, record, node, rowIndex, e) {
  144. this.selectItem(record);
  145. },
  146. /**
  147. * Handles a keypress event on the picker element
  148. * @private
  149. * @param {Ext.EventObject} e
  150. * @param {HTMLElement} el
  151. */
  152. onPickerKeypress: function(e, el) {
  153. var key = e.getKey();
  154. if(key === e.ENTER || (key === e.TAB && this.selectOnTab)) {
  155. this.selectItem(this.picker.getSelectionModel().getSelection()[0]);
  156. }
  157. },
  158. /**
  159. * Changes the selection to a given record and closes the picker
  160. * @private
  161. * @param {Ext.data.Model} record
  162. */
  163. selectItem: function(record) {
  164. var me = this;
  165. me.setValue(record.get('id'));
  166. me.picker.hide();
  167. me.inputEl.focus();
  168. me.fireEvent('select', me, record)
  169. },
  170. /**
  171. * Runs when the picker is expanded. Selects the appropriate tree node based on the value of the input element,
  172. * and focuses the picker so that keyboard navigation will work.
  173. * @private
  174. */
  175. onExpand: function() {
  176. var me = this,
  177. picker = me.picker,
  178. store = picker.store,
  179. value = me.value;
  180. if(value) {
  181. picker.selectPath(store.getNodeById(value).getPath());
  182. } else {
  183. picker.getSelectionModel().select(store.getRootNode());
  184. }
  185. Ext.defer(function() {
  186. picker.getView().focus();
  187. }, 1);
  188. },
  189. /**
  190. * Sets the specified value into the field
  191. * @param {Mixed} value
  192. * @return {Ext.ux.TreePicker} this
  193. */
  194. setValue: function(value) {
  195. var me = this,
  196. record;
  197. me.value = value;
  198. if (me.store.loading) {
  199. // Called while the Store is loading. Ensure it is processed by the onLoad method.
  200. return me;
  201. }
  202. // try to find a record in the store that matches the value
  203. record = value ? me.store.getNodeById(value) : me.store.getRootNode();
  204. // set the raw value to the record's display field if a record was found
  205. me.setRawValue(record ? record.get(this.displayField) : '');
  206. return me;
  207. },
  208. /**
  209. * Returns the current data value of the field (the idProperty of the record)
  210. * @return {Number}
  211. */
  212. getValue: function() {
  213. return this.value;
  214. },
  215. /**
  216. * Handles the store's load event.
  217. * @private
  218. */
  219. onLoad: function() {
  220. var value = this.value;
  221. if (value) {
  222. this.setValue(value);
  223. }
  224. }
  225. });