Unverified Commit b0dfce31 authored by Hannah Stepanek's avatar Hannah Stepanek Committed by GitHub

Merge pull request #1254 from hypothesis/convert-search-status-bar

Convert search status bar to preact
parents 30d4004e cdd54488
'use strict';
const memoize = require('../util/memoize');
const { Fragment, createElement } = require('preact');
const propTypes = require('prop-types');
const { useMemo } = require('preact/hooks');
const { withServices } = require('../util/service-context');
const uiConstants = require('../ui-constants');
const useStore = require('../store/use-store');
// @ngInject
function SearchStatusBarController(store, rootThread) {
this.TAB_ANNOTATIONS = uiConstants.TAB_ANNOTATIONS;
this.TAB_NOTES = uiConstants.TAB_NOTES;
this.TAB_ORPHANS = uiConstants.TAB_ORPHANS;
const thread = () => {
return rootThread.thread(store.getState());
};
const visibleCount = memoize(thread => {
return thread.children.reduce(
const countVisibleAnns = annThread => {
return annThread.children.reduce(
function(count, child) {
return count + visibleCount(child);
return count + countVisibleAnns(child);
},
thread.visible ? 1 : 0
annThread.visible ? 1 : 0
);
});
};
/**
* A bar where the user can clear a selection or search and see whether
* any search results were found.
* */
function SearchStatusBar({
selectedTab,
totalAnnotations,
totalNotes,
rootThread,
}) {
const storeState = useStore(store => store.getState());
const clearSelection = useStore(store => store.clearSelection);
const filterQuery = storeState.filterQuery;
const filterActive = !!storeState.filterQuery;
this.filterMatchCount = function() {
return visibleCount(thread());
const thread = rootThread.thread(storeState);
const visibleCount = useMemo(() => {
return countVisibleAnns(thread);
}, [thread]);
const filterResults = () => {
const resultsCount = visibleCount;
switch (resultsCount) {
case 0:
return 'No results for "' + filterQuery + '"';
case 1:
return '1 search result';
default:
return resultsCount + ' search results';
}
};
this.areAllAnnotationsVisible = function() {
if (store.getState().directLinkedGroupFetchFailed) {
const areNotAllAnnotationsVisible = () => {
if (storeState.directLinkedGroupFetchFailed) {
return true;
}
const selection = store.getState().selectedAnnotationMap;
const selection = storeState.selectedAnnotationMap;
if (!selection) {
return false;
}
return Object.keys(selection).length > 0;
};
this.filterQuery = function() {
return store.getState().filterQuery;
};
this.filterActive = function() {
return !!store.getState().filterQuery;
};
this.onClearSelection = function() {
store.clearSelection();
};
return (
<div>
{filterActive && (
<div className="search-status-bar">
<button
className="primary-action-btn primary-action-btn--short"
onClick={clearSelection}
title="Clear the search filter and show all annotations"
>
<i className="primary-action-btn__icon h-icon-close" />
Clear search
</button>
<span>{filterResults()}</span>
</div>
)}
{!filterActive && areNotAllAnnotationsVisible() && (
<div className="search-status-bar">
<button
className="primary-action-btn primary-action-btn--short"
onClick={clearSelection}
title="Clear the selection and show all annotations"
>
{selectedTab === uiConstants.TAB_ORPHANS && (
<Fragment>Show all annotations and notes</Fragment>
)}
{selectedTab === uiConstants.TAB_ANNOTATIONS && (
<Fragment>
Show all annotations
{totalAnnotations > 1 && <span> ({totalAnnotations})</span>}
</Fragment>
)}
{selectedTab === uiConstants.TAB_NOTES && (
<Fragment>
Show all notes
{totalNotes > 1 && <span> ({totalNotes})</span>}
</Fragment>
)}
</button>
</div>
)}
</div>
);
}
module.exports = {
controller: SearchStatusBarController,
controllerAs: 'vm',
bindings: {
selectedTab: '<',
totalAnnotations: '<',
totalNotes: '<',
},
template: require('../templates/search-status-bar.html'),
SearchStatusBar.propTypes = {
selectedTab: propTypes.oneOf([
uiConstants.TAB_ANNOTATIONS,
uiConstants.TAB_ORPHANS,
uiConstants.TAB_NOTES,
]).isRequired,
totalAnnotations: propTypes.number.isRequired,
totalNotes: propTypes.number.isRequired,
rootThread: propTypes.object.isRequired,
};
SearchStatusBar.injectedProps = ['rootThread'];
module.exports = withServices(SearchStatusBar);
......@@ -181,7 +181,10 @@ function startAngularApp(config) {
wrapReactComponent(require('./components/moderation-banner'))
)
.component('newNoteBtn', require('./components/new-note-btn'))
.component('searchStatusBar', require('./components/search-status-bar'))
.component(
'searchStatusBar',
wrapReactComponent(require('./components/search-status-bar'))
)
.component('selectionTabs', require('./components/selection-tabs'))
.component('sidebarContent', require('./components/sidebar-content'))
.component(
......
<div class="search-status-bar" ng-if="vm.filterActive()">
<button class="primary-action-btn primary-action-btn--short"
ng-click="vm.onClearSelection()"
title="Clear the search filter and show all annotations"
>
<i class="primary-action-btn__icon h-icon-close"></i> Clear search
</button>
<span ng-pluralize
count="vm.filterMatchCount()"
when="{'0': 'No results for “{{vm.filterQuery()}}”',
'one': '1 search result',
'other': '{} search results'}"></span>
</div>
<div class="search-status-bar" ng-if="!vm.filterActive() && vm.areAllAnnotationsVisible()">
<button class="primary-action-btn primary-action-btn--short"
ng-click="vm.onClearSelection()"
title="Clear the selection and show all annotations">
<span ng-if="!vm.selectedTab || vm.selectedTab === vm.TAB_ORPHANS">
Show all annotations and notes
</span>
<span ng-if="vm.selectedTab === vm.TAB_ANNOTATIONS">
Show all annotations
<span ng-if="vm.totalAnnotations > 1">
({{vm.totalAnnotations}})
</span>
</span>
<span ng-if="vm.selectedTab === vm.TAB_NOTES">
Show all notes
<span ng-if="vm.totalNotes > 1">
({{vm.totalNotes}})
</span>
</span>
</button>
</div>
......@@ -9,7 +9,8 @@
</selection-tabs>
<search-status-bar
ng-show="!vm.isLoading()"
class="search-status-bar"
ng-if="!vm.isLoading()"
selected-tab="vm.selectedTab"
total-annotations="vm.totalAnnotations"
total-notes="vm.totalNotes">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment