@performant-software/semantic-components 1.0.17 → 1.0.18-beta.0
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.
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/package.json +6 -6
- package/src/components/BibliographyForm.js +121 -0
- package/src/components/BibliographyList.js +21 -2
- package/src/components/BibliographyModal.js +18 -112
- package/src/components/BibliographySearchInput.js +13 -0
- package/src/components/CurrentFacetLabels.js +4 -0
- package/src/components/CurrentFacets.js +6 -0
- package/src/components/CurrentFacetsModal.js +14 -0
- package/src/components/Facet.js +19 -1
- package/src/components/FacetClearButton.js +6 -2
- package/src/components/FacetList.js +43 -2
- package/src/components/FacetSlider.js +7 -5
- package/src/components/FacetToggle.js +5 -0
- package/src/components/SearchBox.js +7 -3
- package/src/components/SearchPagination.js +8 -0
- package/src/components/SearchResults.js +39 -0
- package/src/components/SearchResultsPerPage.js +7 -0
- package/src/components/SearchStats.js +4 -0
- package/src/components/StyleSelector.js +12 -1
- package/src/i18n/en.json +2 -0
- package/src/index.js +3 -0
- package/src/types/InstantSearch.js +30 -0
- package/types/components/BibliographyForm.js.flow +121 -0
- package/types/components/BibliographyList.js.flow +21 -2
- package/types/components/BibliographyModal.js.flow +18 -112
- package/types/components/BibliographySearchInput.js.flow +13 -0
- package/types/components/CurrentFacetLabels.js.flow +4 -0
- package/types/components/CurrentFacets.js.flow +6 -0
- package/types/components/CurrentFacetsModal.js.flow +14 -0
- package/types/components/Facet.js.flow +19 -1
- package/types/components/FacetClearButton.js.flow +6 -2
- package/types/components/FacetList.js.flow +43 -2
- package/types/components/FacetSlider.js.flow +7 -5
- package/types/components/FacetToggle.js.flow +5 -0
- package/types/components/SearchBox.js.flow +7 -3
- package/types/components/SearchPagination.js.flow +8 -0
- package/types/components/SearchResults.js.flow +39 -0
- package/types/components/SearchResultsPerPage.js.flow +7 -0
- package/types/components/SearchStats.js.flow +4 -0
- package/types/components/StyleSelector.js.flow +12 -1
- package/types/index.js.flow +3 -0
- package/types/types/InstantSearch.js.flow +30 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@performant-software/semantic-components",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18-beta.0",
|
|
4
4
|
"description": "A package of shared components based on the Semantic UI Framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./build/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"build": "webpack --mode production && flow-copy-source -v src types"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@performant-software/shared-components": "^1.0.
|
|
15
|
+
"@performant-software/shared-components": "^1.0.18-beta.0",
|
|
16
16
|
"@react-google-maps/api": "^2.8.1",
|
|
17
17
|
"axios": "^0.26.1",
|
|
18
18
|
"i18next": "^19.4.4",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"zotero-translation-client": "^5.0.1"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"react": ">= 16.13.1 <
|
|
36
|
-
"react-dom": ">= 16.13.1 <
|
|
35
|
+
"react": ">= 16.13.1 < 19.0.0",
|
|
36
|
+
"react-dom": ">= 16.13.1 < 19.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@performant-software/webpack-config": "^1.0.0",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"less": "^4.1.2",
|
|
42
42
|
"less-loader": "^11.0.0",
|
|
43
43
|
"mini-css-extract-plugin": "^2.6.0",
|
|
44
|
-
"react": "^
|
|
45
|
-
"react-dom": "^
|
|
44
|
+
"react": "^18.2.0",
|
|
45
|
+
"react-dom": "^18.2.0"
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type { EditContainerProps } from '@performant-software/shared-components/types';
|
|
4
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
5
|
+
import { Form } from 'semantic-ui-react';
|
|
6
|
+
import _ from 'underscore';
|
|
7
|
+
import BibliographyUtils from '../utils/Bibliography';
|
|
8
|
+
import Creators from './Creators';
|
|
9
|
+
|
|
10
|
+
type Props = EditContainerProps & {
|
|
11
|
+
/**
|
|
12
|
+
* A JSON representation of the bibliographic item.
|
|
13
|
+
*/
|
|
14
|
+
item: any
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Field constants
|
|
18
|
+
const FIELD_ABSTRACT_NOTE = 'abstractNote';
|
|
19
|
+
const FIELD_CREATORS = 'creators';
|
|
20
|
+
const FIELD_EXTRA = 'extra';
|
|
21
|
+
const FIELD_ITEM_TYPE = 'itemType';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* This component can be used in conjunction with the `useEditContainer` higher-order component to create a form
|
|
25
|
+
* used to enter bibliographic data.
|
|
26
|
+
*/
|
|
27
|
+
const BibliographyForm = (props: Props) => {
|
|
28
|
+
const [creators, setCreators] = useState([]);
|
|
29
|
+
const [fields, setFields] = useState([]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Renders the input element for the passed field.
|
|
33
|
+
*
|
|
34
|
+
* @type {(function(*): *)|*}
|
|
35
|
+
*/
|
|
36
|
+
const renderField = useCallback((field) => {
|
|
37
|
+
if (field.key === FIELD_ITEM_TYPE) {
|
|
38
|
+
return (
|
|
39
|
+
<Form.Dropdown
|
|
40
|
+
disabled={field.readonly}
|
|
41
|
+
key={field.key}
|
|
42
|
+
label={field.label}
|
|
43
|
+
onChange={props.onTextInputChange.bind(this, field.key)}
|
|
44
|
+
options={_.map(field.options, (option) => ({
|
|
45
|
+
key: option.value,
|
|
46
|
+
value: option.value,
|
|
47
|
+
text: option.label
|
|
48
|
+
}))}
|
|
49
|
+
selectOnBlur={false}
|
|
50
|
+
selection
|
|
51
|
+
value={props.item[field.key] || ''}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (field.key === FIELD_CREATORS) {
|
|
57
|
+
return (
|
|
58
|
+
<Creators
|
|
59
|
+
creatorTypes={creators}
|
|
60
|
+
key={field.key}
|
|
61
|
+
onChange={(value) => props.onSetState({ [field.key]: value })}
|
|
62
|
+
value={props.item[field.key] || []}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (field.key === FIELD_ABSTRACT_NOTE || field.key === FIELD_EXTRA) {
|
|
68
|
+
return (
|
|
69
|
+
<Form.TextArea
|
|
70
|
+
disabled={field.readonly}
|
|
71
|
+
key={field.key}
|
|
72
|
+
label={field.label}
|
|
73
|
+
onChange={props.onTextInputChange.bind(this, field.key)}
|
|
74
|
+
value={props.item[field.key] || ''}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Form.Input
|
|
81
|
+
disabled={field.readonly}
|
|
82
|
+
key={field.key}
|
|
83
|
+
label={field.label}
|
|
84
|
+
onChange={props.onTextInputChange.bind(this, field.key)}
|
|
85
|
+
value={props.item[field.key] || ''}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
}, [creators, props.item]);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Load the metadata for the selected item type.
|
|
92
|
+
*/
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
BibliographyUtils
|
|
95
|
+
.getItemTypeMeta(props.item.itemType)
|
|
96
|
+
.then((data) => {
|
|
97
|
+
const itemTypes = _.map(data.itemTypes, (it) => ({
|
|
98
|
+
value: it.itemType,
|
|
99
|
+
label: it.localized
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
const itemTypeCreatorTypes = _.map(data.itemTypeCreatorTypes, (ct) => ({
|
|
103
|
+
value: ct.creatorType,
|
|
104
|
+
label: ct.localized
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
setCreators(itemTypeCreatorTypes);
|
|
108
|
+
|
|
109
|
+
const bibliographyData = BibliographyUtils.getFieldsAndItem(props.item, data.itemTypeFields, itemTypes);
|
|
110
|
+
setFields(bibliographyData.fields);
|
|
111
|
+
});
|
|
112
|
+
}, [props.item.itemType]);
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<>
|
|
116
|
+
{ _.map(fields, renderField) }
|
|
117
|
+
</>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default BibliographyForm;
|
|
@@ -23,7 +23,7 @@ import SortSelector from './SortSelector';
|
|
|
23
23
|
import { SORT_DESCENDING } from '../constants/Sort';
|
|
24
24
|
import StyleSelector from './StyleSelector';
|
|
25
25
|
import Toaster from './Toaster';
|
|
26
|
-
import useList from './List';
|
|
26
|
+
import useList, { Props as ListProps } from './List';
|
|
27
27
|
import ZoteroTranslateContext from '../context/ZoteroTranslateContext';
|
|
28
28
|
import './BibliographyList.css';
|
|
29
29
|
|
|
@@ -110,13 +110,32 @@ const BibliographyListComponent: ComponentType<any> = useList((props: ComponentP
|
|
|
110
110
|
</List>
|
|
111
111
|
));
|
|
112
112
|
|
|
113
|
-
type Props = {
|
|
113
|
+
type Props = ListProps & {
|
|
114
|
+
/**
|
|
115
|
+
* The array of bibliography items.
|
|
116
|
+
*/
|
|
114
117
|
items: Array<Item>,
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Callback fired when the passed item is deleted. This function should return a Promise.
|
|
121
|
+
*/
|
|
115
122
|
onDelete: (item: Item) => Promise<any>,
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Callback fired when the passed item is saved. This function should return a Promise.
|
|
126
|
+
*/
|
|
116
127
|
onSave: (item: Item) => Promise<any>,
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* URL of the Zotero translation server.
|
|
131
|
+
*/
|
|
117
132
|
translateUrl: string
|
|
118
133
|
};
|
|
119
134
|
|
|
135
|
+
/**
|
|
136
|
+
* This component renders a list of bibliography items in Zbib format. This component requires the URL of a running
|
|
137
|
+
* [Zotero Translation Server](https://github.com/zotero/translation-server) to be passed as the `translateUrl` prop.
|
|
138
|
+
*/
|
|
120
139
|
const BibliographyList = (props: Props) => {
|
|
121
140
|
const [locale, setLocale] = useState();
|
|
122
141
|
const [showToaster, setShowToaster] = useState(false);
|
|
@@ -1,125 +1,31 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
|
|
3
3
|
import type { EditContainerProps } from '@performant-software/shared-components/types';
|
|
4
|
-
import React
|
|
4
|
+
import React from 'react';
|
|
5
5
|
import { Form, Modal } from 'semantic-ui-react';
|
|
6
|
-
import _ from 'underscore';
|
|
7
|
-
import BibliographyUtils from '../utils/Bibliography';
|
|
8
|
-
import Creators from './Creators';
|
|
9
6
|
import i18n from '../i18n/i18n';
|
|
7
|
+
import BibliographyForm from './BibliographyForm';
|
|
10
8
|
|
|
11
9
|
type Props = EditContainerProps & {
|
|
12
10
|
item: any
|
|
13
11
|
};
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
* @type {(function(*): *)|*}
|
|
29
|
-
*/
|
|
30
|
-
const renderField = useCallback((field) => {
|
|
31
|
-
if (field.key === FIELD_ITEM_TYPE) {
|
|
32
|
-
return (
|
|
33
|
-
<Form.Dropdown
|
|
34
|
-
disabled={field.readonly}
|
|
35
|
-
key={field.key}
|
|
36
|
-
label={field.label}
|
|
37
|
-
onChange={props.onTextInputChange.bind(this, field.key)}
|
|
38
|
-
options={_.map(field.options, (option) => ({
|
|
39
|
-
key: option.value,
|
|
40
|
-
value: option.value,
|
|
41
|
-
text: option.label
|
|
42
|
-
}))}
|
|
43
|
-
selectOnBlur={false}
|
|
44
|
-
selection
|
|
45
|
-
value={props.item[field.key] || ''}
|
|
46
|
-
/>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (field.key === FIELD_CREATORS) {
|
|
51
|
-
return (
|
|
52
|
-
<Creators
|
|
53
|
-
creatorTypes={creators}
|
|
54
|
-
key={field.key}
|
|
55
|
-
onChange={(value) => props.onSetState({ [field.key]: value })}
|
|
56
|
-
value={props.item[field.key] || []}
|
|
57
|
-
/>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (field.key === FIELD_ABSTRACT_NOTE || field.key === FIELD_EXTRA) {
|
|
62
|
-
return (
|
|
63
|
-
<Form.TextArea
|
|
64
|
-
disabled={field.readonly}
|
|
65
|
-
key={field.key}
|
|
66
|
-
label={field.label}
|
|
67
|
-
onChange={props.onTextInputChange.bind(this, field.key)}
|
|
68
|
-
value={props.item[field.key] || ''}
|
|
69
|
-
/>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<Form.Input
|
|
75
|
-
disabled={field.readonly}
|
|
76
|
-
key={field.key}
|
|
77
|
-
label={field.label}
|
|
78
|
-
onChange={props.onTextInputChange.bind(this, field.key)}
|
|
79
|
-
value={props.item[field.key] || ''}
|
|
80
|
-
/>
|
|
81
|
-
);
|
|
82
|
-
}, [creators, props.item]);
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Load the metadata for the selected item type.
|
|
86
|
-
*/
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
BibliographyUtils
|
|
89
|
-
.getItemTypeMeta(props.item.itemType)
|
|
90
|
-
.then((data) => {
|
|
91
|
-
const itemTypes = _.map(data.itemTypes, (it) => ({
|
|
92
|
-
value: it.itemType,
|
|
93
|
-
label: it.localized
|
|
94
|
-
}));
|
|
95
|
-
|
|
96
|
-
const itemTypeCreatorTypes = _.map(data.itemTypeCreatorTypes, (ct) => ({
|
|
97
|
-
value: ct.creatorType,
|
|
98
|
-
label: ct.localized
|
|
99
|
-
}));
|
|
100
|
-
|
|
101
|
-
setCreators(itemTypeCreatorTypes);
|
|
102
|
-
|
|
103
|
-
const bibliographyData = BibliographyUtils.getFieldsAndItem(props.item, data.itemTypeFields, itemTypes);
|
|
104
|
-
setFields(bibliographyData.fields);
|
|
105
|
-
});
|
|
106
|
-
}, [props.item.itemType]);
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<Modal
|
|
110
|
-
as={Form}
|
|
111
|
-
centered={false}
|
|
112
|
-
open
|
|
113
|
-
>
|
|
114
|
-
<Modal.Header
|
|
115
|
-
content={i18n.t('BibliographyModal.title')}
|
|
13
|
+
const BibliographyModal = (props: Props) => (
|
|
14
|
+
<Modal
|
|
15
|
+
as={Form}
|
|
16
|
+
centered={false}
|
|
17
|
+
open
|
|
18
|
+
>
|
|
19
|
+
<Modal.Header
|
|
20
|
+
content={i18n.t('BibliographyModal.title')}
|
|
21
|
+
/>
|
|
22
|
+
<Modal.Content>
|
|
23
|
+
<BibliographyForm
|
|
24
|
+
{...props}
|
|
116
25
|
/>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
</Modal>
|
|
122
|
-
);
|
|
123
|
-
};
|
|
26
|
+
</Modal.Content>
|
|
27
|
+
{ props.children }
|
|
28
|
+
</Modal>
|
|
29
|
+
);
|
|
124
30
|
|
|
125
31
|
export default BibliographyModal;
|
|
@@ -9,10 +9,23 @@ import ZoteroTranslateContext from '../context/ZoteroTranslateContext';
|
|
|
9
9
|
import './BibliographySearchInput.css';
|
|
10
10
|
|
|
11
11
|
type Props = {
|
|
12
|
+
/**
|
|
13
|
+
* Callback fired when the translator encounters an error.
|
|
14
|
+
*/
|
|
12
15
|
onError: () => void,
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Callback fired when the translator is successful.
|
|
19
|
+
* @param items
|
|
20
|
+
*/
|
|
13
21
|
onFind: (items: array<any>) => void
|
|
14
22
|
};
|
|
15
23
|
|
|
24
|
+
/**
|
|
25
|
+
* This component renders a search input and can be used within a `ZoteroTranslateContext` to lookup sources by URL,
|
|
26
|
+
* ISBN, DOI, and more identifiers. The `ZoteroTranslateContext` requires a running
|
|
27
|
+
* [Zotero Translation Server](https://github.com/zotero/translation-server) to be passed as the `translateUrl` key.
|
|
28
|
+
*/
|
|
16
29
|
const BibliographySearchInput = (props: Props) => {
|
|
17
30
|
const [loading, setLoading] = useState(false);
|
|
18
31
|
const [searchQuery, setSearchQuery] = useState('');
|
|
@@ -15,6 +15,10 @@ type Props = {
|
|
|
15
15
|
onShowMore?: () => void
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* This component displays the passed items as labels. If a <code>count</code> prop is provided, the component
|
|
20
|
+
* will display a "+" button.
|
|
21
|
+
*/
|
|
18
22
|
const CurrentFacetLabels = (props: Props) => (
|
|
19
23
|
<Label.Group>
|
|
20
24
|
{ _.map(props.items, (item, index) => (
|
|
@@ -7,9 +7,15 @@ import CurrentFacetsModal from './CurrentFacetsModal';
|
|
|
7
7
|
import { type CurrentRefinementsProps } from '../types/InstantSearch';
|
|
8
8
|
|
|
9
9
|
type Props = CurrentRefinementsProps & {
|
|
10
|
+
/**
|
|
11
|
+
* The maximum number of facets to display. If more facets are added, they will be accessible via modal.
|
|
12
|
+
*/
|
|
10
13
|
limit?: number
|
|
11
14
|
};
|
|
12
15
|
|
|
16
|
+
/**
|
|
17
|
+
* This component can be used to display the facets/refinements currently applied to an InstantSearch index.
|
|
18
|
+
*/
|
|
13
19
|
const CurrentFacets = ({ useCurrentRefinements, ...props }: Props) => {
|
|
14
20
|
const [modal, setModal] = useState(false);
|
|
15
21
|
const { items } = useCurrentRefinements(props);
|
|
@@ -6,11 +6,25 @@ import CurrentFacetLabels, { type Item } from './CurrentFacetLabels';
|
|
|
6
6
|
import i18n from '../i18n/i18n';
|
|
7
7
|
|
|
8
8
|
type Props = {
|
|
9
|
+
/**
|
|
10
|
+
* An array of facet values.
|
|
11
|
+
*/
|
|
9
12
|
items: Array<Item>,
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Callback fired when the "Done" button is clicked.
|
|
16
|
+
*/
|
|
10
17
|
onClose: () => void,
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* If `true` the modal will be visible.
|
|
21
|
+
*/
|
|
11
22
|
open?: boolean
|
|
12
23
|
};
|
|
13
24
|
|
|
25
|
+
/**
|
|
26
|
+
* This component displays all of the facets currently applied, without any limit.
|
|
27
|
+
*/
|
|
14
28
|
const CurrentFacetsModal = (props: Props) => (
|
|
15
29
|
<Modal
|
|
16
30
|
centered={false}
|
package/src/components/Facet.js
CHANGED
|
@@ -10,12 +10,30 @@ import {
|
|
|
10
10
|
import './Facet.css';
|
|
11
11
|
|
|
12
12
|
type Props = {
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Facet content to display inside the accordion menu.
|
|
15
|
+
*/
|
|
16
|
+
children: Node,
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* If `true`, the facet will be expanded by default.
|
|
20
|
+
*/
|
|
14
21
|
defaultActive?: boolean,
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* If `true`, a divider will be rendered between each facet in the list.
|
|
25
|
+
*/
|
|
15
26
|
divided?: boolean,
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Facet title to display at the top.
|
|
30
|
+
*/
|
|
16
31
|
title: string
|
|
17
32
|
};
|
|
18
33
|
|
|
34
|
+
/**
|
|
35
|
+
* This component can be used as a wrapper to display various types of facets (list, toggle, etc).
|
|
36
|
+
*/
|
|
19
37
|
const Facet = (props: Props) => {
|
|
20
38
|
const [active, setActive] = useState(props.defaultActive);
|
|
21
39
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { Button, ButtonProps } from 'semantic-ui-react';
|
|
4
|
+
import { Button, type ButtonProps } from 'semantic-ui-react';
|
|
5
5
|
import { type ClearRefinementsProps } from '../types/InstantSearch';
|
|
6
6
|
|
|
7
|
-
type Props =
|
|
7
|
+
type Props = ButtonProps & ClearRefinementsProps;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* This component is used with the `useClearRefinements` hook from Instant Search Hooks. This component also accepts
|
|
11
|
+
* all of the props of the Semantic UI <code>Button</code> component.
|
|
12
|
+
*/
|
|
9
13
|
const FacetClearButton = ({ useClearRefinements, ...props }: Props) => {
|
|
10
14
|
const { refine, canRefine } = useClearRefinements(props);
|
|
11
15
|
|
|
@@ -21,11 +21,37 @@ import LinkButton from './LinkButton';
|
|
|
21
21
|
import { type RefinementListProps } from '../types/InstantSearch';
|
|
22
22
|
|
|
23
23
|
type Props = FacetProps & RefinementListProps & {
|
|
24
|
+
/**
|
|
25
|
+
* The default value for the `operator` prop. If not provided, this will default to `or`.
|
|
26
|
+
*/
|
|
27
|
+
defaultOperator?: string,
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Default value of the facet list.
|
|
31
|
+
*/
|
|
24
32
|
defaultValue?: string,
|
|
25
|
-
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* If "true", the component will render a search box for searching individual facet values.
|
|
36
|
+
*/
|
|
37
|
+
searchable?: boolean,
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* If "true", the component will render a toggle to change the behavior of the list from "or" to "and" logic.
|
|
41
|
+
*/
|
|
42
|
+
toggleable?: boolean
|
|
26
43
|
};
|
|
27
44
|
|
|
45
|
+
const OPERATOR_OR = 'or';
|
|
46
|
+
const OPERATOR_AND = 'and';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* This component is used with the `useRefinementList` hook from Instant Search Hooks. If the `searchable` prop
|
|
50
|
+
* is "true", the component will also render a search box used to filter the list of facet values.
|
|
51
|
+
*/
|
|
28
52
|
const FacetList = ({ useRefinementList, ...props }: Props) => {
|
|
53
|
+
const [operator, setOperator] = useState(props.defaultOperator || OPERATOR_OR);
|
|
54
|
+
|
|
29
55
|
const {
|
|
30
56
|
items,
|
|
31
57
|
refine,
|
|
@@ -33,7 +59,7 @@ const FacetList = ({ useRefinementList, ...props }: Props) => {
|
|
|
33
59
|
isShowingMore,
|
|
34
60
|
searchForItems,
|
|
35
61
|
toggleShowMore,
|
|
36
|
-
} = useRefinementList(props);
|
|
62
|
+
} = useRefinementList({ ...props, operator });
|
|
37
63
|
|
|
38
64
|
const ref = useRef();
|
|
39
65
|
const [query, setQuery] = useState('');
|
|
@@ -152,8 +178,23 @@ const FacetList = ({ useRefinementList, ...props }: Props) => {
|
|
|
152
178
|
/>
|
|
153
179
|
</>
|
|
154
180
|
)}
|
|
181
|
+
{ props.toggleable && (
|
|
182
|
+
<Checkbox
|
|
183
|
+
checked={operator === OPERATOR_AND}
|
|
184
|
+
label={operator === OPERATOR_OR
|
|
185
|
+
? i18n.t('FacetList.labels.matchAny')
|
|
186
|
+
: i18n.t('FacetList.labels.matchAll')}
|
|
187
|
+
onClick={() => setOperator((prevOperator) => (prevOperator === OPERATOR_OR ? OPERATOR_AND : OPERATOR_OR))}
|
|
188
|
+
toggle
|
|
189
|
+
/>
|
|
190
|
+
)}
|
|
155
191
|
</Facet>
|
|
156
192
|
);
|
|
157
193
|
};
|
|
158
194
|
|
|
195
|
+
FacetList.defaultProps = {
|
|
196
|
+
...Facet.defaultProps,
|
|
197
|
+
defaultOperator: OPERATOR_OR
|
|
198
|
+
};
|
|
199
|
+
|
|
159
200
|
export default FacetList;
|
|
@@ -4,11 +4,15 @@ import Slider from 'rc-slider';
|
|
|
4
4
|
import React, { useEffect, useState } from 'react';
|
|
5
5
|
import { Grid } from 'semantic-ui-react';
|
|
6
6
|
import Facet, { type Props as FacetProps } from './Facet';
|
|
7
|
-
import { type
|
|
7
|
+
import { type RangeSliderProps } from '../types/InstantSearch';
|
|
8
|
+
import 'rc-slider/assets/index.css';
|
|
8
9
|
import './FacetSlider.css';
|
|
9
10
|
|
|
10
|
-
type Props = FacetProps &
|
|
11
|
+
type Props = FacetProps & RangeSliderProps;
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* This component can be used with the `useRange` hook from Instant Search Hooks.
|
|
15
|
+
*/
|
|
12
16
|
const FacetSlider = ({ useRangeSlider, ...props }: Props) => {
|
|
13
17
|
const {
|
|
14
18
|
start,
|
|
@@ -74,8 +78,6 @@ const FacetSlider = ({ useRangeSlider, ...props }: Props) => {
|
|
|
74
78
|
);
|
|
75
79
|
};
|
|
76
80
|
|
|
77
|
-
FacetSlider.defaultProps =
|
|
78
|
-
defaultValue: undefined
|
|
79
|
-
};
|
|
81
|
+
FacetSlider.defaultProps = Facet.defaultProps;
|
|
80
82
|
|
|
81
83
|
export default FacetSlider;
|
|
@@ -7,6 +7,9 @@ import { type ToggleRefinementProps } from '../types/InstantSearch';
|
|
|
7
7
|
|
|
8
8
|
type Props = FacetProps & ToggleRefinementProps;
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* This component is used with the `useToggleRefinement` hook from Instant Search Hooks.
|
|
12
|
+
*/
|
|
10
13
|
const FacetToggle = ({ useToggleRefinement, ...props }: Props) => {
|
|
11
14
|
const {
|
|
12
15
|
value: {
|
|
@@ -44,4 +47,6 @@ const FacetToggle = ({ useToggleRefinement, ...props }: Props) => {
|
|
|
44
47
|
);
|
|
45
48
|
};
|
|
46
49
|
|
|
50
|
+
FacetToggle.defaultProps = Facet.defaultProps;
|
|
51
|
+
|
|
47
52
|
export default FacetToggle;
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import { Timer } from '@performant-software/shared-components';
|
|
4
4
|
import React, { useCallback, useRef, useState } from 'react';
|
|
5
|
-
import { Icon, Input, InputProps } from 'semantic-ui-react';
|
|
5
|
+
import { Icon, Input, type InputProps } from 'semantic-ui-react';
|
|
6
6
|
import _ from 'underscore';
|
|
7
7
|
import { type SearchBoxProps } from '../types/InstantSearch';
|
|
8
8
|
|
|
9
|
-
type Props =
|
|
9
|
+
type Props = InputProps & SearchBoxProps;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* This component is used with the `useSearchBox` function from Instant Search Hooks and renders an input element that
|
|
13
|
+
* when changed will execute a new query.
|
|
14
|
+
*/
|
|
15
|
+
const SearchBox = ({ useSearchBox, ...props }: Props) => {
|
|
12
16
|
const {
|
|
13
17
|
query,
|
|
14
18
|
refine,
|
|
@@ -5,9 +5,17 @@ import { Pagination } from 'semantic-ui-react';
|
|
|
5
5
|
import { type PaginationProps } from '../types/InstantSearch';
|
|
6
6
|
|
|
7
7
|
type Props = PaginationProps & {
|
|
8
|
+
/**
|
|
9
|
+
* If `true`, we'll scroll to the top of the page after applying the new page value.
|
|
10
|
+
*/
|
|
8
11
|
scrollToTop?: boolean
|
|
9
12
|
};
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* This component is used with the `usePagination` hook from Instant Search Hooks and renders a list of page numbers
|
|
16
|
+
* that can be selected by the user. If the `scrollToTop` prop is set to `true`, the window will scroll to the top
|
|
17
|
+
* after applying a new value.
|
|
18
|
+
*/
|
|
11
19
|
const SearchPagination = ({ usePagination, ...props }: Props) => {
|
|
12
20
|
const { currentRefinement, nbPages: pages, refine } = usePagination(props);
|
|
13
21
|
const onPageChange = useCallback((e, { activePage }) => refine(activePage - 1), [refine]);
|