@girder/core 3.1.0 → 3.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,6 +48,11 @@ var Collection = Backbone.Collection.extend({
48
48
  */
49
49
  pageOffsetStack: null,
50
50
 
51
+ /**
52
+ * Number of items in the collection returned by 'girder-total-count' header
53
+ */
54
+ _totalCount: 0,
55
+
51
56
  /**
52
57
  * Returns a boolean of whether or not this collection has previous pages,
53
58
  * i.e. if the offset of the current page start is > 0
@@ -69,7 +74,15 @@ var Collection = Backbone.Collection.extend({
69
74
  hasNextPage: function () {
70
75
  return this._hasMorePages;
71
76
  },
72
-
77
+ /**
78
+ * This value is populated whenever the list length exceeds the pageLimit.
79
+ * It is used to determine how many pages are needed based on the page limit
80
+ * it is retrieved from the response header 'girder-total-count'
81
+ * @returns {number} total number of items retrieved
82
+ */
83
+ getTotalCount: function () {
84
+ return this._totalCount || 0;
85
+ },
73
86
  /**
74
87
  * Fetch the previous page of this collection, emitting g:changed when done.
75
88
  */
@@ -103,7 +116,7 @@ var Collection = Backbone.Collection.extend({
103
116
  },
104
117
 
105
118
  /**
106
- * Return the 0-indexed page number of the current page. Add 1 to this
119
+ * @returns {number} the 0-indexed page number of the current page. Add 1 to this
107
120
  * result when displaying it to the user.
108
121
  *
109
122
  * If this collection hasn't been fully initialized (i.e.: before any pages
@@ -119,6 +132,20 @@ var Collection = Backbone.Collection.extend({
119
132
  return Math.ceil((this.offset - this.length) / this.pageLimit);
120
133
  },
121
134
 
135
+ /**
136
+ * Sets a specific pagenumber for loading by caluclating the offset
137
+ * @param {Number} pageNumber The 0 indexed page that should be loaded based on the pageLimit size
138
+ * @param {Object} params Additional parameters to pass to the fetch call
139
+ * @returns {Promise} a fetch promise to retrieve more data
140
+ */
141
+ fetchPage: function (pageNumber, params) {
142
+ // Make sure the page Number is within range, pageNumber is indexed at 0
143
+ if (!this.append && pageNumber * this.pageLimit < this._totalCount && pageNumber >= 0) {
144
+ this.offset = pageNumber * this.pageLimit;
145
+ return this.fetch(_.extend({}, this.params, params || {}));
146
+ }
147
+ },
148
+
122
149
  /**
123
150
  * Fetches the next page of this collection, replacing the existing models
124
151
  * of this collection with the requested page. If the next page contains
@@ -169,6 +196,7 @@ var Collection = Backbone.Collection.extend({
169
196
  // This means we have more pages to display still. Pop off
170
197
  // the extra that we fetched.
171
198
  list.pop();
199
+ this._totalCount = xhr.getResponseHeader('girder-total-count');
172
200
  this._hasMorePages = true;
173
201
  } else {
174
202
  this._hasMorePages = false;
@@ -16,7 +16,7 @@ var UserCollection = Collection.extend({
16
16
  method: 'GET'
17
17
  })
18
18
  .then((resp) => {
19
- return resp['nUsers'];
19
+ return resp.nUsers;
20
20
  });
21
21
  }
22
22
  });
package/dialog.js CHANGED
@@ -92,7 +92,7 @@ function confirm(params) {
92
92
  } else {
93
93
  el.text(params.text);
94
94
  }
95
- if (params['msgConfirmation']) {
95
+ if (params.msgConfirmation) {
96
96
  if (params.escapedHtml) {
97
97
  $('.g-additional-text').html(params.additionalText);
98
98
  } else {
@@ -101,7 +101,7 @@ function confirm(params) {
101
101
  }
102
102
 
103
103
  $('#g-confirm-button').off('click').on('click', function () {
104
- if (params['msgConfirmation']) {
104
+ if (params.msgConfirmation) {
105
105
  const key = `${params.yesText.toUpperCase()} ${params.name}`;
106
106
  const msg = $('#g-confirm-text').val();
107
107
  if (msg.toUpperCase() === key.toUpperCase()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@girder/core",
3
- "version": "3.1.0",
3
+ "version": "3.1.12",
4
4
  "description": "Extensible data management platform",
5
5
  "homepage": "https://girder.readthedocs.org",
6
6
  "bugs": {
@@ -18,7 +18,7 @@
18
18
  "bootstrap-switch": "~3.3.4",
19
19
  "eonasdan-bootstrap-datetimepicker": "~4.17",
20
20
  "@girder/fontello": "*",
21
- "jquery": "~3.2.1",
21
+ "jquery": "~3.5.1",
22
22
  "jsoneditor": "~5.9.3",
23
23
  "moment": "~2.24.0",
24
24
  "moment-timezone": "~0.5.27",
@@ -1,6 +1,10 @@
1
1
  .g-hierarchy-widget
2
2
  border 1px solid #eaeaea
3
3
 
4
+ .g-hierarchy-sticky
5
+ position sticky
6
+ z-index 10
7
+
4
8
  .g-hierarchy-actions-header
5
9
  background linear-gradient(to bottom, #e6e6e6 0, #f7f7f7 8px)
6
10
  padding 4px 5px 3px
@@ -21,6 +25,26 @@
21
25
  .g-folder-header-buttons
22
26
  float right
23
27
 
28
+ .g-hierarachy-paginated-bar
29
+ background-color #eaebea
30
+ .g-hierarchy-pages-header
31
+ .g-hierarchycenter
32
+ margin auto
33
+ text-align center
34
+ .pagination
35
+ display inline-block
36
+ vertical-align middle
37
+ margin 0
38
+ padding 0
39
+ border-radius 4px
40
+ .g-page-next, .g-page-prev
41
+ a
42
+ padding 8px 12px
43
+ #g-page-selection-input
44
+ margin-left 3px
45
+ margin-right 3px
46
+
47
+
24
48
  .g-hierarchy-breadcrumb-bar
25
49
  ol.breadcrumb
26
50
  margin-bottom 0
@@ -68,6 +92,10 @@
68
92
  right -3px
69
93
  padding 0 0.3em
70
94
 
95
+ .g-folder-list-container, .g-item-list-container
96
+ position relative
97
+ z-index 0
98
+
71
99
  .g-folder-list, .g-item-list, .g-file-list
72
100
  margin 0
73
101
  padding 0
@@ -0,0 +1,19 @@
1
+ .g-hierarchy-pages-header
2
+ .g-hierarchycenter
3
+ ul.pagination.pagination-sm
4
+ if (currentPage >1 && !disabled)
5
+ li.g-page-prev
6
+ a#g-previous-paginated « Prev
7
+ else
8
+ li.g-page-prev.disabled
9
+ a#g-previous-paginated « Prev
10
+ li
11
+ span Page:
12
+ input#g-page-selection-input(disabled=disabled, type='number', min=1, max=totalPages, value=currentPage)
13
+ span of #{totalPages}
14
+ if (currentPage+1<=totalPages && !disabled)
15
+ li.g-page-next
16
+ a#g-next-paginated Next »
17
+ else
18
+ li.g-page-next.disabled
19
+ a#g-next-paginated Next »
@@ -91,5 +91,6 @@
91
91
  | This folder has no suitable items.
92
92
  else
93
93
  | This folder is empty.
94
+ .g-hierarachy-paginated-bar
94
95
  if showMetadata
95
96
  .g-folder-metadata
@@ -20,7 +20,7 @@ ul.g-item-list
20
20
  i.icon-eye
21
21
  if showSizes
22
22
  .g-item-size= formatSize(item.get('size'))
23
- if (hasMore)
23
+ if (hasMore && !paginated)
24
24
  li.g-show-more
25
25
  a.g-show-more-items
26
26
  i.icon-level-down
@@ -187,7 +187,7 @@ var SystemConfigurationView = View.extend({
187
187
  });
188
188
  } else {
189
189
  settingValue = this.settings['core.collection_create_policy'];
190
- settingValue['open'] = false;
190
+ settingValue.open = false;
191
191
  }
192
192
  return settingValue;
193
193
  }
@@ -58,6 +58,7 @@ var BrowserWidget = View.extend({
58
58
  error message) if the selection is unacceptable.
59
59
  * @param {string} [input.placeholder] A placeholder string for the input element.
60
60
  * @param {boolean} [highlightItem=false] highlights the selected item in the hierarchy and scrolls to it.
61
+ * @param {boolean} [paginated=false] the browser widget is paginated
61
62
  */
62
63
  initialize: function (settings) {
63
64
  // store options
@@ -74,6 +75,7 @@ var BrowserWidget = View.extend({
74
75
  this.selectItem = !!settings.selectItem;
75
76
  this.showMetadata = !!settings.showMetadata;
76
77
  this.highlightItem = !!settings.highlightItem;
78
+ this.paginated = !!settings.paginated;
77
79
  this._selected = null;
78
80
 
79
81
  // Sets RootSelectorWidget to open properly to the root if not already set in the settings
@@ -148,6 +150,7 @@ var BrowserWidget = View.extend({
148
150
  onItemClick: _.bind(this._selectItem, this),
149
151
  defaultSelectedResource: this.defaultSelectedResource,
150
152
  highlightItem: this.highlightItem,
153
+ paginated: this.paginated,
151
154
  showMetadata: this.showMetadata
152
155
  });
153
156
  this.listenTo(this._hierarchyView, 'g:setCurrentModel', this._selectModel);
@@ -23,6 +23,7 @@ import { getModelClassByName, renderMarkdown, formatCount, capitalize, formatSiz
23
23
  import { restRequest, getApiRoot } from '@girder/core/rest';
24
24
 
25
25
  import HierarchyBreadcrumbTemplate from '@girder/core/templates/widgets/hierarchyBreadcrumb.pug';
26
+ import HierarchyPaginatedTemplate from '@girder/core/templates/widgets/hierarchyPaginated.pug';
26
27
  import HierarchyWidgetTemplate from '@girder/core/templates/widgets/hierarchyWidget.pug';
27
28
 
28
29
  import '@girder/core/stylesheets/widgets/hierarchyWidget.styl';
@@ -67,6 +68,51 @@ var HierarchyBreadcrumbView = View.extend({
67
68
  }
68
69
  });
69
70
 
71
+ var HierarchyPaginatedView = View.extend({
72
+ events: {
73
+ 'change #g-page-selection-input': function (event) {
74
+ const num = Number(event.target.value);
75
+ if (this.itemListWidget && num <= this.itemListWidget.getNumPages() && num > 0) {
76
+ this.disabled = true;
77
+ this.itemListWidget.setPage(num).done(() => {
78
+ this.disabled = false;
79
+ this.render();
80
+ });
81
+ }
82
+ this.render();
83
+ },
84
+ 'click li.g-page-next:not(.disabled) a#g-next-paginated': function () {
85
+ this.disabled = true;
86
+ this.itemListWidget.setPage(Number(this.$('#g-page-selection-input').val()) + 1).done(() => {
87
+ this.disabled = false;
88
+ this.render();
89
+ });
90
+ this.render();
91
+ },
92
+ 'click li.g-page-prev:not(.disabled) a#g-previous-paginated': function () {
93
+ this.disabled = true;
94
+ this.itemListWidget.setPage(Number(this.$('#g-page-selection-input').val()) - 1).done(() => {
95
+ this.disabled = false;
96
+ this.render();
97
+ });
98
+ this.render();
99
+ }
100
+ },
101
+ initialize: function (settings) {
102
+ this.itemListWidget = settings.itemListWidget;
103
+ this.disabled = false;
104
+ },
105
+ render: function () {
106
+ this.$el.html(HierarchyPaginatedTemplate({
107
+ totalPages: this.itemListWidget && this.itemListWidget.getNumPages(),
108
+ currentPage: this.itemListWidget && this.itemListWidget.getCurrentPage(),
109
+ disabled: this.disabled
110
+ }));
111
+
112
+ return this;
113
+ }
114
+ });
115
+
70
116
  /**
71
117
  * This widget is used to navigate the data hierarchy of folders and items.
72
118
  */
@@ -114,6 +160,7 @@ var HierarchyWidget = View.extend({
114
160
  * event as its second.
115
161
  * [defaultSelectedResource] : default selected Resource item , will open up to this resource
116
162
  * [highlightItem=false] : sets the item to be styled as selected and will scroll to it in the list
163
+ * [paginated=false] : sets the itemlist view to be paginated, will set appendPages to false
117
164
  */
118
165
  initialize: function (settings) {
119
166
  this.parentModel = settings.parentModel;
@@ -136,7 +183,7 @@ var HierarchyWidget = View.extend({
136
183
  };
137
184
  this._defaultSelectedResource = settings.defaultSelectedResource;
138
185
  this._highlightItem = _.has(settings, 'highlightItem') ? settings.highlightItem : false;
139
-
186
+ this._paginated = _.has(settings, 'paginated') ? settings.paginated : false;
140
187
  this._onFolderSelect = settings.onFolderSelect;
141
188
 
142
189
  this.folderAccess = settings.folderAccess;
@@ -224,13 +271,34 @@ var HierarchyWidget = View.extend({
224
271
  showSizes: this._showSizes,
225
272
  selectedItem: this._defaultSelectedResource,
226
273
  highlightItem: this._highlightItem,
274
+ paginated: this._paginated,
227
275
  parentView: this
228
276
  });
229
277
  this.listenTo(this.itemListView, 'g:itemClicked', this._onItemClick);
230
278
  this.listenTo(this.itemListView, 'g:checkboxesChanged', this.updateChecked);
279
+
231
280
  this.listenTo(this.itemListView, 'g:changed', () => {
232
281
  this.itemCount = this.itemListView.collection.length;
233
282
  this._childCountCheck();
283
+ if (this._paginated && this.hierarchyPaginated && this.itemListView.getNumPages() > 1) {
284
+ this.render();
285
+ this.$('.g-hierarchy-breadcrumb-bar').addClass('g-hierarchy-sticky');
286
+ this.$('.g-hierarachy-paginated-bar').addClass('g-hierarchy-sticky');
287
+ this.$('.g-hierarchy-breadcrumb-bar').css({ top: 0 });
288
+ this.$('.g-hierarachy-paginated-bar').css({ bottom: 0 });
289
+ } else {
290
+ // We remove the bar if the current folder doesn't have more than one page, keep the sticky breadcrumb though
291
+ this.$('.g-hierarachy-paginated-bar').remove();
292
+ }
293
+ });
294
+ // Only emitted when there is more than one page of data
295
+ this.listenTo(this.itemListView, 'g:paginated', () => {
296
+ if (this._paginated && !this.hierarchyPaginated) {
297
+ this.hierarchyPaginated = new HierarchyPaginatedView({
298
+ parentView: this,
299
+ itemListWidget: this.itemListView
300
+ });
301
+ }
234
302
  });
235
303
  }
236
304
 
@@ -289,7 +357,9 @@ var HierarchyWidget = View.extend({
289
357
  this.checkedMenuWidget.dropdownToggle = this.$('.g-checked-actions-button');
290
358
  this.checkedMenuWidget.setElement(this.$('.g-checked-actions-menu')).render();
291
359
  this.folderListView.setElement(this.$('.g-folder-list-container')).render();
292
-
360
+ if (this.hierarchyPaginated && this.parentModel.resourceName !== 'collection') {
361
+ this.hierarchyPaginated.setElement(this.$('.g-hierarachy-paginated-bar')).render();
362
+ }
293
363
  if (this.parentModel.resourceName === 'folder' && this._showItems) {
294
364
  this._initFolderViewSubwidgets();
295
365
  this.itemListView.setElement(this.$('.g-item-list-container')).render();
@@ -547,6 +617,7 @@ var HierarchyWidget = View.extend({
547
617
  viewLinks: this._viewLinks,
548
618
  itemFilter: this._itemFilter,
549
619
  showSizes: this._showSizes,
620
+ paginated: this._paginated,
550
621
  public: this.parentModel.get('public'),
551
622
  accessLevel: this.parentModel.getAccessLevel()
552
623
  });
@@ -613,6 +684,7 @@ var HierarchyWidget = View.extend({
613
684
  yesText: 'Delete',
614
685
  confirmCallback: () => {
615
686
  var resources = this._getCheckedResourceParam();
687
+ var resourceSize = this._getCheckedResourceSize();
616
688
  /* Content on DELETE requests is somewhat oddly supported (I
617
689
  * can't get it to work under jasmine/phantom), so override the
618
690
  * method. */
@@ -628,7 +700,9 @@ var HierarchyWidget = View.extend({
628
700
  if (folders.length && this.parentModel.has('nFolders')) {
629
701
  this.parentModel.increment('nFolders', -folders.length);
630
702
  }
631
-
703
+ if (this.parentModel.has('size') && resourceSize.items) {
704
+ this.parentModel.increment('size', -resourceSize.items);
705
+ }
632
706
  this.setCurrentModel(this.parentModel, { setRoute: false });
633
707
  });
634
708
  }
@@ -784,6 +858,26 @@ var HierarchyWidget = View.extend({
784
858
  return JSON.stringify(resources);
785
859
  },
786
860
 
861
+ /**
862
+ * Get the sum of the sizes of checked resources.
863
+ */
864
+ _getCheckedResourceSize: function () {
865
+ var totalSize = { items: 0, folders: 0 };
866
+ var folders = this.folderListView.checked;
867
+ _.each(folders, function (cid) {
868
+ var folder = this.folderListView.collection.get(cid);
869
+ totalSize.folders += (folder.get('size') || 0);
870
+ }, this);
871
+ if (this.itemListView) {
872
+ var items = this.itemListView.checked;
873
+ _.each(items, function (cid) {
874
+ var item = this.itemListView.collection.get(cid);
875
+ totalSize.items += (item.get('size') || 0);
876
+ }, this);
877
+ }
878
+ return totalSize;
879
+ },
880
+
787
881
  downloadChecked: function () {
788
882
  var url = getApiRoot() + '/resource/download';
789
883
  var resources = this._getCheckedResourceParam();
@@ -5,6 +5,7 @@ import ItemCollection from '@girder/core/collections/ItemCollection';
5
5
  import LoadingAnimation from '@girder/core/views/widgets/LoadingAnimation';
6
6
  import View from '@girder/core/views/View';
7
7
  import { formatSize } from '@girder/core/misc';
8
+ import { restRequest } from '@girder/core/rest';
8
9
 
9
10
  import ItemListTemplate from '@girder/core/templates/widgets/itemList.pug';
10
11
 
@@ -47,6 +48,8 @@ var ItemListWidget = View.extend({
47
48
  _.has(settings, 'showSizes') ? settings.showSizes : true);
48
49
  this._highlightItem = (
49
50
  _.has(settings, 'highlightItem') ? settings.highlightItem : false);
51
+ this._paginated = (
52
+ _.has(settings, 'paginated') ? settings.paginated : false);
50
53
 
51
54
  this.accessLevel = settings.accessLevel;
52
55
  this.public = settings.public;
@@ -60,20 +63,74 @@ var ItemListWidget = View.extend({
60
63
  this.collection = new ItemCollection();
61
64
  this.collection.append = true; // Append, don't replace pages
62
65
  this.collection.filterFunc = settings.itemFilter;
66
+ this.currentPage = 1; // By default we want to be on the first page
63
67
 
64
- this.collection.on('g:changed', function () {
65
- if (this.accessLevel !== undefined) {
66
- this.collection.each((model) => {
67
- model.set('_accessLevel', this.accessLevel);
68
- });
68
+ if (this._paginated) {
69
+ if (this.collection.filterFunc) {
70
+ console.warn('Pagination cannot be used with a filter function');
71
+ this._paginated = false;
72
+ } else {
73
+ // Override the default to prevent appending new pages
74
+ this.collection.append = false;
69
75
  }
70
- this.render();
71
- this.trigger('g:changed');
72
- }, this).fetch({ folderId: settings.folderId });
76
+ }
77
+
78
+ this.collection.fetch({ folderId: settings.folderId }).done(() => {
79
+ this._totalPages = Math.ceil(this.collection.getTotalCount() / this.collection.pageLimit);
80
+ if (this._paginated && this.collection.hasNextPage) {
81
+ // Tells the parent container that the item is paginated so it can render the page selector
82
+ this.trigger('g:paginated');
83
+ // We need to get the position in the list for the selected item
84
+ if (this._selectedItem) {
85
+ restRequest({
86
+ url: `item/${this._selectedItem.get('_id')}/position`,
87
+ method: 'GET',
88
+ data: { folderId: this._selectedItem.get('folderId'), sort: 'name' }
89
+ }).done((val) => {
90
+ // Now we fetch the correct page for the position
91
+ val = Number(val);
92
+ if (val >= this.collection.pageLimit) {
93
+ const pageLimit = this.collection.pageLimit;
94
+ const calculatedPage = 1 + Math.ceil((val - (val % pageLimit)) / pageLimit);
95
+ return this.collection.fetchPage(calculatedPage - 1);
96
+ }
97
+ }).done(() => this.bindOnChanged());
98
+ } else {
99
+ this.bindOnChanged();
100
+ }
101
+ } else {
102
+ this.bindOnChanged();
103
+ }
104
+ });
105
+ },
106
+
107
+ /**
108
+ * Binds the change function to the collection and calls it initially to update the render
109
+ */
110
+ bindOnChanged: function () {
111
+ this.collection.on('g:changed', this.changedFunc, this);
112
+ this.changedFunc();
113
+ },
114
+ /**
115
+ * Function that causes a render each time the collection is changed
116
+ * Will also update the current page in a paginated system
117
+ */
118
+ changedFunc: function () {
119
+ if (this.accessLevel !== undefined) {
120
+ this.collection.each((model) => {
121
+ model.set('_accessLevel', this.accessLevel);
122
+ });
123
+ }
124
+ if (this._paginated) {
125
+ this.currentPage = this.collection.pageNum() + 1;
126
+ }
127
+ this.render();
128
+ this.trigger('g:changed');
73
129
  },
74
130
 
75
131
  render: function () {
76
132
  this.checked = [];
133
+ // If we set a selected item in the beginning we will center the selection while loading
77
134
  if (this._selectedItem && this._highlightItem) {
78
135
  this.scrollPositionObserver();
79
136
  }
@@ -88,13 +145,37 @@ var ItemListWidget = View.extend({
88
145
  viewLinks: this._viewLinks,
89
146
  showSizes: this._showSizes,
90
147
  highlightItem: this._highlightItem,
91
- selectedItemId: (this._selectedItem || {}).id
148
+ selectedItemId: (this._selectedItem || {}).id,
149
+ paginated: this._paginated
150
+
92
151
  }));
93
152
 
94
- // If we set a selected item in the beginning we will center the selection while loading
95
153
  return this;
96
154
  },
97
155
 
156
+ /**
157
+ * @returns {number} the number of pages in the itemList for use in a paginated view
158
+ */
159
+ getNumPages() {
160
+ return this._totalPages || 1;
161
+ },
162
+ /**
163
+ * @returns {number} the current page for paginated lists, defaults to 1 if none is provided
164
+ */
165
+ getCurrentPage() {
166
+ return this.currentPage || 1;
167
+ },
168
+ /**
169
+ * Externally facing function to allow hierarchyWidget and others to set the current page if the item is paginated
170
+ * @param {Number} page 1 index integer specifying the page to fetch
171
+ */
172
+ setPage(page) {
173
+ if (this._paginated && this.collection && this.collection.fetchPage) {
174
+ this.currentPage = page;
175
+ return this.collection.fetchPage(page - 1).then(() =>
176
+ this.$el.parents('.g-hierarchy-widget-container').scrollTop(0));
177
+ }
178
+ },
98
179
  /**
99
180
  * Insert an item into the collection and re-render it.
100
181
  */
@@ -156,11 +237,16 @@ var ItemListWidget = View.extend({
156
237
  centerSelected: function () {
157
238
  const widgetcontainer = this.$el.parents('.g-hierarchy-widget-container');
158
239
  const selected = this.$('li.g-item-list-entry.g-selected');
159
-
160
240
  if (widgetcontainer.length > 0 && selected.length > 0) {
161
- const centerPos = (widgetcontainer.height() / 2.0) + (selected.outerHeight() / 2.0);
162
- // Take the current scroll top position and add it to the position of the top of the selected item, then center it
163
- const scrollPos = widgetcontainer[0].scrollTop + selected.position().top - centerPos;
241
+ // These items will effect the scroll position if they exists
242
+ const folderHeight = $('.g-folder-list').length ? $('.g-folder-list').height() : 0;
243
+ const breadcrumbHeight = $('.g-hierarchy-breadcrumb-bar').length ? $('.g-hierarchy-breadcrumb-bar').height() : 0;
244
+ const selectedTop = selected.position().top;
245
+ // The selectedTop position needs to account for the breadcrumbHeight and the folderHeight
246
+ const scrollingPos = selectedTop + folderHeight + breadcrumbHeight;
247
+ const centerPos = (widgetcontainer.height() / 2.0) - (folderHeight / 2.0) - (breadcrumbHeight / 2.0) - (selected.outerHeight() / 2.0);
248
+
249
+ const scrollPos = scrollingPos - centerPos;
164
250
  if (this.tempScrollPos === undefined) {
165
251
  this.tempScrollPos = scrollPos;
166
252
  }
@@ -234,7 +320,7 @@ var ItemListWidget = View.extend({
234
320
  this.observer.disconnect();
235
321
  this.observer = null;
236
322
  this.tempScrollPos = undefined;
237
- // Prevents scrolling once clicking has more
323
+ // Prevents scrolling when user clicks 'show more...'
238
324
  this._highlightItem = false;
239
325
  }
240
326
  };