From 5b246542881dddd22d5343174f4c7b8bca0d55aa Mon Sep 17 00:00:00 2001 From: Tom Clegg Date: Sun, 13 Aug 2017 01:50:55 -0400 Subject: [PATCH] 12033: Restore search term and scroll position after navigation. Arvados-DCO-1.1-Signed-off-by: Tom Clegg --- .../javascripts/components/collections.js | 76 +++++++++++-------- .../javascripts/components/save_state.js | 47 ++++++++++++ 2 files changed, 93 insertions(+), 30 deletions(-) create mode 100644 apps/workbench/app/assets/javascripts/components/save_state.js diff --git a/apps/workbench/app/assets/javascripts/components/collections.js b/apps/workbench/app/assets/javascripts/components/collections.js index 9e1db750b5..98c2ab0af7 100644 --- a/apps/workbench/app/assets/javascripts/components/collections.js +++ b/apps/workbench/app/assets/javascripts/components/collections.js @@ -25,6 +25,7 @@ window.components.collection_table = { window.addEventListener('scroll', vnode.state.maybeLoadMore) window.addEventListener('resize', vnode.state.maybeLoadMore) vnode.state.timer = window.setInterval(vnode.state.maybeLoadMore, 200) + vnode.state.loader = vnode.attrs.loader vnode.state.onupdate(vnode) }, onupdate: function(vnode) { @@ -76,9 +77,14 @@ window.components.collection_table = { window.components.collection_search = { oninit: function(vnode) { vnode.state.sessionDB = new window.models.SessionDB() - vnode.state.searchEntered = m.stream('') - vnode.state.searchStart = m.stream('') - vnode.state.searchStart.map(function(q) { + vnode.state.searchEntered = m.stream() + vnode.state.searchActive = m.stream() + // When searchActive changes (e.g., when restoring state + // after navigation), update the text field too. + vnode.state.searchActive.map(vnode.state.searchEntered) + // When searchActive changes, create a new loader that filters + // with the given search term. + vnode.state.searchActive.map(function(q) { vnode.state.loader = new window.models.MultisiteLoader({ loadFunc: function(session, filters) { if (q) @@ -98,39 +104,49 @@ window.components.collection_search = { var sessions = vnode.state.sessionDB.loadAll() return m('form', { onsubmit: function() { - vnode.state.searchStart(vnode.state.searchEntered()) + vnode.state.searchActive(vnode.state.searchEntered()) + vnode.state.forgetSavedState = true return false }, }, [ - m('.row', [ - m('.col-md-6', [ - m('.input-group', [ - m('input#search.form-control[placeholder=Search]', { - oninput: m.withAttr('value', vnode.state.searchEntered), - }), - m('.input-group-btn', [ - m('input.btn.btn-primary[type=submit][value="Search"]'), + m(window.components.save_state, { + defaultState: '', + currentState: vnode.state.searchActive, + forgetSavedState: vnode.state.forgetSavedState, + saveBodyHeight: true, + }), + vnode.state.loader && [ + m('.row', [ + m('.col-md-6', [ + m('.input-group', [ + m('input#search.form-control[placeholder=Search]', { + oninput: m.withAttr('value', vnode.state.searchEntered), + value: vnode.state.searchEntered(), + }), + m('.input-group-btn', [ + m('input.btn.btn-primary[type=submit][value="Search"]'), + ]), ]), ]), + m('.col-md-6', [ + 'Searching sites: ', + Object.keys(sessions).length == 0 + ? m('span.label.label-xs.label-danger', 'none') + : Object.keys(sessions).sort().map(function(key) { + return [m('span.label.label-xs', { + className: !vnode.state.loader.pagers[key] ? 'label-default' : + vnode.state.loader.pagers[key].items() ? 'label-success' : + 'label-warning', + }, key), ' '] + }), + ' ', + m('a[href="/sessions"]', 'Add/remove sites'), + ]), ]), - m('.col-md-6', [ - 'Searching sites: ', - Object.keys(sessions).length == 0 - ? m('span.label.label-xs.label-danger', 'none') - : Object.keys(sessions).sort().map(function(key) { - return [m('span.label.label-xs', { - className: !vnode.state.loader.pagers[key] ? 'label-default' : - vnode.state.loader.pagers[key].items() ? 'label-success' : - 'label-warning', - }, key), ' '] - }), - ' ', - m('a[href="/sessions"]', 'Add/remove sites'), - ]), - ]), - m(window.components.collection_table, { - loader: vnode.state.loader, - }), + m(window.components.collection_table, { + loader: vnode.state.loader, + }), + ], ]) }, } diff --git a/apps/workbench/app/assets/javascripts/components/save_state.js b/apps/workbench/app/assets/javascripts/components/save_state.js new file mode 100644 index 0000000000..021f7ff18e --- /dev/null +++ b/apps/workbench/app/assets/javascripts/components/save_state.js @@ -0,0 +1,47 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +window.components = window.components || {} +window.components.save_state = { + saveState: function() { + var state = history.state || {} + state.bodyHeight = window.getComputedStyle(document.body)['height'] + state.currentState = this.currentState() + history.replaceState(state, '') + }, + oninit: function(vnode) { + vnode.state.currentState = vnode.attrs.currentState + var hstate = history.state || {} + + if (vnode.attrs.saveBodyHeight && hstate.bodyHeight) { + document.body.style['min-height'] = hstate.bodyHeight + delete hstate.bodyHeight + } + + if (hstate.currentState) { + vnode.attrs.currentState(hstate.currentState) + delete hstate.currentState + } else { + vnode.attrs.currentState(vnode.attrs.defaultState) + } + + history.replaceState(hstate, '') + }, + oncreate: function(vnode) { + vnode.state.saveState = vnode.state.saveState.bind(vnode.state) + window.addEventListener('beforeunload', vnode.state.saveState) + vnode.state.onupdate(vnode) + }, + onupdate: function(vnode) { + if (vnode.attrs.saveBodyHeight && vnode.attrs.forgetSavedState) { + document.body.style['min-height'] = null + } + }, + onremove: function(vnode) { + window.removeEventListener('beforeunload', vnode.state.saveState) + }, + view: function(vnode) { + return null + }, +} -- 2.30.2