diff --git a/CHANGELOG.md b/CHANGELOG.md index b28835e5a..a88574d3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Support named item lists ([#1665](../../pull/1665)) - Add options to group within item lists ([#1666](../../pull/1666)) - Make the filter field in item lists wider ([#1669](../../pull/1669)) +- Add a navigate option to item lists ([#1659](../../pull/1659)) ### Changes diff --git a/docs/girder_config_options.rst b/docs/girder_config_options.rst index 5c113a227..4243d45f5 100644 --- a/docs/girder_config_options.rst +++ b/docs/girder_config_options.rst @@ -90,6 +90,15 @@ This is used to specify how items appear in item lists. There are two settings, counts: dicom.StudyInstanceUID: _count.studiescount dicom.SeriesInstanceUID: _count.seriescount + # navigate does nto need to be specified. It changes the behavior of + # clicking on an item from showing the item page to another action. + navigate: + # type can be "item": the default, open the item page, "itemList": go + # to the named item page + type: itemList + # if the type is "itemList", the name is the name of the itemList to + # display. + name: studyList # show these columns in order from left to right. Each column has a # "type" and "value". It optionally has a "title" used for the column # header, and a "format" used for searching and filtering. The "label", diff --git a/girder/girder_large_image/web_client/templates/itemList.pug b/girder/girder_large_image/web_client/templates/itemList.pug index 87ac8c36f..bcbbde9b2 100644 --- a/girder/girder_large_image/web_client/templates/itemList.pug +++ b/girder/girder_large_image/web_client/templates/itemList.pug @@ -43,7 +43,7 @@ ul.g-item-list.li-item-list(layout_mode=(itemList.layout || {}).mode || '', meta skip = true; } }); - #{divtype}.li-item-list-cell(class=classes.join(' '), g-item-cid=item.cid, href=`#item/${item.id}`, title=colNames[colidx]) + #{divtype}.li-item-list-cell(class=classes.join(' '), g-item-cid=item.cid, href=item._href ? item._href : `#item/${item.id}`, title=colNames[colidx]) if !skip && column.label span.g-item-list-label = column.label diff --git a/girder/girder_large_image/web_client/views/itemList.js b/girder/girder_large_image/web_client/views/itemList.js index d04da9b0f..770df2f8b 100644 --- a/girder/girder_large_image/web_client/views/itemList.js +++ b/girder/girder_large_image/web_client/views/itemList.js @@ -20,11 +20,23 @@ import {MetadatumWidget, validateMetadataValue} from './metadataWidget'; ItemCollection.prototype.pageLimit = Math.max(250, ItemCollection.prototype.pageLimit); +function onItemClick(item) { + if (this.itemListView && this.itemListView.onItemClick) { + if (this.itemListView.onItemClick(item)) { + return; + } + } + router.navigate('item/' + item.get('_id'), {trigger: true}); +} + wrap(HierarchyWidget, 'initialize', function (initialize, settings) { settings = settings || {}; if (settings.paginated === undefined) { settings.paginated = true; } + if (settings.onItemClick === undefined) { + settings.onItemClick = onItemClick; + } return initialize.call(this, settings); }); @@ -232,6 +244,60 @@ wrap(ItemListWidget, 'render', function (render) { } }; + /** + * Return true if we handle the click + */ + this.onItemClick = (item) => { + const list = this._confList(); + const nav = (list || {}).navigate; + if (!nav || (!nav.type && !nav.name) || nav.type === 'item') { + return false; + } + if (nav.type === 'itemList') { + if ((nav.name || '') === (self._namedList || '')) { + return false; + } + if (!this._liconfig || !this._liconfig.namedItemLists || (nav.name && !this._liconfig.namedItemLists[nav.name])) { + return false; + } + this._updateNamedList(nav.name, false); + if (list.group) { + this._generalFilter = ''; + list.group.keys.forEach((key) => { + const cell = this.$el.find(`[g-item-cid="${item.cid}"] [column-value="${key}"]`); + if (cell.length) { + addCellToFilter.call(this, cell, false); + } + }); + } + this._setFilter(false); + this._setSort(); + addToRoute({namedList: this._namedList, filter: this._generalFilter}); + return true; + } + if (nav.type === 'open') { + // TODO: handle open type + // we probably need to get all the grouped items to pass them to + // the .open-in-volview button via that _getCheckedResourceParam + // call OR modify the volview plugin to have an open item with less + // context. The current folder context would ideally be the + // deepest common parent rather than our current folder. Where + // does volview store its zip file? + } + return false; + }; + + this._updateNamedList = (name, update) => { + name = name || ''; + if ((this._namedList || '') !== name) { + this._namedList = name; + if (update !== false) { + addToRoute({namedList: this._namedList}); + this._setSort(); + } + } + }; + this._updateFilter = (evt) => { this._generalFilter = $(evt.target).val().trim(); this._setFilter(); @@ -380,6 +446,53 @@ wrap(ItemListWidget, 'render', function (render) { } }; + /** + * For each item in the collection, if we are navigating to something other + * than the item, set an href property. + */ + function adjustItemHref() { + this.collection.forEach((item) => { + item._href = undefined; + }); + const list = this._confList(); + const nav = (list || {}).navigate; + if (!nav || (!nav.type && !nav.name) || nav.type === 'item') { + return; + } + if (nav.type === 'itemList') { + if ((nav.name || '') === (self._namedList || '')) { + return; + } + if (!this._liconfig || !this._liconfig.namedItemLists || (nav.name && !this._liconfig.namedItemLists[nav.name])) { + return; + } + this.collection.forEach((item) => { + item._href = `#folder/${this.parentView.parentModel.id}?namedList=` + (nav.name ? encodeURIComponent(nav.name) : ''); + let filter = ''; + if (list.group) { + list.group.keys.forEach((col) => { + let val = item.get('meta') || {}; + col.split('.').forEach((part) => { + val = (val || {})[part]; + }); + if (/[ '\\]/.exec(col)) { + col = "'" + col.replace('\\', '\\\\').replace("'", "\\'") + "'"; + } + if (val) { + val = val.replace('\\', '\\\\').replace('"', '\\"'); + filter += ` ${col}:"${val}"`; + } + }); + } + filter = filter.trim(); + if (filter !== '') { + item._href += '&filter=' + encodeURIComponent(filter); + } + }); + } + // TODO: handle nav.type open + } + function itemListRender() { if (this._inInit || this._inFetch) { return; @@ -422,6 +535,7 @@ wrap(ItemListWidget, 'render', function (render) { this._setSort(); return; } + adjustItemHref.call(this); this.$el.html(ItemListTemplate({ items: this.collection.toArray(), isParentPublic: this.public,