@performant-software/core-data 1.2.0-beta.2 → 1.2.0-beta.20

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.
Files changed (42) hide show
  1. package/build/index.js +1 -1
  2. package/build/index.js.map +1 -1
  3. package/build/main.css +17 -5
  4. package/package.json +13 -3
  5. package/postcss.config.js +6 -0
  6. package/src/components/MediaGallery.js +1 -1
  7. package/src/components/PlaceDetailsPanel.js +14 -1
  8. package/src/components/PlaceMarker.js +6 -0
  9. package/src/components/PlaceResultsList.css +7 -0
  10. package/src/components/PlaceResultsList.js +178 -0
  11. package/src/components/{PlaceDetailsPanel.css → RelatedItemsList.css} +5 -5
  12. package/src/components/RelatedItemsList.js +14 -3
  13. package/src/components/RelatedList.js +15 -1
  14. package/src/components/RelatedMedia.js +14 -0
  15. package/src/components/RelatedOrganizations.js +8 -1
  16. package/src/components/RelatedPeople.js +8 -1
  17. package/src/components/RelatedPlaces.js +8 -1
  18. package/src/components/RelatedTaxonomies.js +8 -1
  19. package/src/i18n/en.json +27 -0
  20. package/src/i18n/i18n.js +26 -0
  21. package/src/index.css +3 -0
  22. package/src/index.js +4 -0
  23. package/src/types/Feature.js +10 -0
  24. package/src/types/typesense/Place.js +14 -0
  25. package/tailwind.config.js +19 -0
  26. package/types/components/MediaGallery.js.flow +1 -1
  27. package/types/components/PlaceDetailsPanel.js.flow +14 -1
  28. package/types/components/PlaceMarker.js.flow +6 -0
  29. package/types/components/PlaceResultsList.js.flow +178 -0
  30. package/types/components/RelatedItemsList.js.flow +14 -3
  31. package/types/components/RelatedList.js.flow +15 -1
  32. package/types/components/RelatedMedia.js.flow +14 -0
  33. package/types/components/RelatedOrganizations.js.flow +8 -1
  34. package/types/components/RelatedPeople.js.flow +8 -1
  35. package/types/components/RelatedPlaces.js.flow +8 -1
  36. package/types/components/RelatedTaxonomies.js.flow +8 -1
  37. package/types/components/SearchResultsList.js.flow +160 -0
  38. package/types/i18n/i18n.js.flow +26 -0
  39. package/types/index.js.flow +4 -0
  40. package/types/types/Feature.js.flow +10 -0
  41. package/types/types/typesense/Place.js.flow +14 -0
  42. package/webpack.config.js +31 -1
@@ -2,18 +2,25 @@
2
2
 
3
3
  import { Building2 } from 'lucide-react';
4
4
  import React from 'react';
5
+ import i18n from '../i18n/i18n';
5
6
  import type { AnnotationPage } from '../types/AnnotationPage';
6
7
  import type { Organization } from '../types/Organization';
7
8
  import RelatedList from './RelatedList';
8
9
 
9
10
  type Props = {
11
+ /**
12
+ * The annotation page containing the Core Data organizations to render.
13
+ */
10
14
  data: AnnotationPage<Organization>
11
15
  };
12
16
 
17
+ /**
18
+ * This component renders the related Core Data organizations records.
19
+ */
13
20
  const RelatedOrganizations = (props: Props) => (
14
21
  <RelatedList
15
22
  data={props.data}
16
- emptyMessage={'No related organization'}
23
+ emptyMessage={i18n.t('RelatedOrganizations.labels.empty')}
17
24
  renderItem={(organization) => (
18
25
  <>
19
26
  <Building2
@@ -2,18 +2,25 @@
2
2
 
3
3
  import { UserCircle } from 'lucide-react';
4
4
  import React from 'react';
5
+ import i18n from '../i18n/i18n';
5
6
  import type { AnnotationPage } from '../types/AnnotationPage';
6
7
  import type { Person } from '../types/Person';
7
8
  import RelatedList from './RelatedList';
8
9
 
9
10
  type Props = {
11
+ /**
12
+ * The annotation page containing the Core Data people to render.
13
+ */
10
14
  data: AnnotationPage<Person>
11
15
  };
12
16
 
17
+ /**
18
+ * This component renders the related Core Data organizations.
19
+ */
13
20
  const RelatedPeople = (props: Props) => (
14
21
  <RelatedList
15
22
  data={props.data}
16
- emptyMessage={'No related people'}
23
+ emptyMessage={i18n.t('RelatedPeople.labels.empty')}
17
24
  renderItem={(person) => (
18
25
  <>
19
26
  <UserCircle
@@ -2,18 +2,25 @@
2
2
 
3
3
  import { MapPin } from 'lucide-react';
4
4
  import React from 'react';
5
+ import i18n from '../i18n/i18n';
5
6
  import type { AnnotationPage } from '../types/AnnotationPage';
6
7
  import type { Place } from '../types/Place';
7
8
  import RelatedList from './RelatedList';
8
9
 
9
10
  type Props = {
11
+ /**
12
+ * The annotation page containing the Core Data places to render.
13
+ */
10
14
  data: AnnotationPage<Place>
11
15
  };
12
16
 
17
+ /**
18
+ * This component renders the related Core Data places.
19
+ */
13
20
  const RelatedPlaces = (props: Props) => (
14
21
  <RelatedList
15
22
  data={props.data}
16
- emptyMessage={'No related places'}
23
+ emptyMessage={i18n.t('RelatedPlaces.labels.empty')}
17
24
  renderItem={(place) => (
18
25
  <>
19
26
  <MapPin
@@ -2,18 +2,25 @@
2
2
 
3
3
  import { ListTree } from 'lucide-react';
4
4
  import React from 'react';
5
+ import i18n from '../i18n/i18n';
5
6
  import type { AnnotationPage } from '../types/AnnotationPage';
6
7
  import RelatedList from './RelatedList';
7
8
  import type { Taxonomy } from '../types/Taxonomy';
8
9
 
9
10
  type Props = {
11
+ /**
12
+ * The annotation page containing the Core Data taxonomies to render.
13
+ */
10
14
  data: AnnotationPage<Taxonomy>
11
15
  };
12
16
 
17
+ /**
18
+ * This component renders the related Core Data taxonomies.
19
+ */
13
20
  const RelatedTaxonomies = (props: Props) => (
14
21
  <RelatedList
15
22
  data={props.data}
16
- emptyMessage={'No related taxonomies'}
23
+ emptyMessage={i18n.t('RelatedTaxonomies.labels.empty')}
17
24
  renderItem={(taxonomy) => (
18
25
  <>
19
26
  <ListTree
@@ -0,0 +1,160 @@
1
+ // @flow
2
+
3
+ import React, { useMemo } from 'react';
4
+ import { Highlight } from 'react-instantsearch';
5
+ import AutoSizer from 'react-virtualized-auto-sizer';
6
+ import { FixedSizeList } from 'react-window';
7
+ import _ from 'underscore';
8
+ import type { Feature } from '../types/Feature';
9
+ import type { Place } from '../types/typesense/Place';
10
+ import './SearchResultsList.css';
11
+
12
+ /**
13
+ * Converts the passed place result to a feature.
14
+ *
15
+ * @param result
16
+ *
17
+ * @returns {{
18
+ * geometry: {coordinates: number[], type: string},
19
+ * id: number,
20
+ * type:
21
+ * string,
22
+ * properties: {
23
+ * ccode: *[],
24
+ * record_id: string,
25
+ * names: *,
26
+ * name: string,
27
+ * id: string,
28
+ * title: string,
29
+ * type: string,
30
+ * uuid: string
31
+ * }
32
+ * }}
33
+ */
34
+ const toFeature = (result: Place) => ({
35
+ id: parseInt(result.record_id, 10),
36
+ type: 'Feature',
37
+ properties: {
38
+ id: result.record_id,
39
+ ccode: [],
40
+ title: result.name,
41
+ uuid: result.uuid,
42
+ record_id: result.record_id,
43
+ name: result.name,
44
+ names: _.map(result.names, (toponym) => ({ toponym })),
45
+ type: result.type
46
+ },
47
+ geometry: {
48
+ type: 'Point',
49
+ coordinates: result.coordinates.slice().reverse()
50
+ }
51
+ });
52
+
53
+ type HitComponentProps = {
54
+ hit: any,
55
+ isHovered: boolean,
56
+ onClick: () => void
57
+ };
58
+
59
+ const HitComponent = (props: HitComponentProps) => {
60
+ const { hit } = props;
61
+
62
+ const className = useMemo(() => {
63
+ const classNames = [
64
+ 'h-[5.5em]',
65
+ 'border-b',
66
+ 'flex',
67
+ 'flex-col',
68
+ 'justify-start'
69
+ ];
70
+
71
+ if (props.isHovered) {
72
+ classNames.add('bg-teal-700/30');
73
+ }
74
+
75
+ return classNames.join(' ');
76
+ }, [props.isHovered]);
77
+
78
+ return (
79
+ <div
80
+ className={className}
81
+ >
82
+ <button
83
+ className='py-2 px-3 flex-grow text-left inline-flex flex-col'
84
+ onClick={props.onClick}
85
+ type='button'
86
+ >
87
+ <Highlight
88
+ attribute='name'
89
+ className='line-clamp-2'
90
+ hit={hit}
91
+ />
92
+ <p
93
+ className='text-muted text-xs line-clamp-1'
94
+ >
95
+ <Highlight
96
+ hit={hit}
97
+ attribute='names'
98
+ />
99
+ </p>
100
+ </button>
101
+ </div>
102
+ );
103
+ };
104
+
105
+ type Props = {
106
+ hits: Array<Place>,
107
+ hover?: Feature<{ id: string }>,
108
+ onHoverChange: (hover?: Feature<{ id: string }>) => void,
109
+ onClick: (result: Place) => void
110
+ };
111
+
112
+ type RowProps = {
113
+ index: number,
114
+ style: any
115
+ };
116
+
117
+ const SearchResultsList = (props: Props) => {
118
+ const {
119
+ hits,
120
+ hover,
121
+ onClick,
122
+ onHoverChange
123
+ } = props;
124
+
125
+ const Row = ({ index, style }: RowProps) => {
126
+ const hit = hits[index];
127
+ const id = parseInt(hit.record_id, 10);
128
+
129
+ return (
130
+ <div
131
+ style={style}
132
+ onPointerEnter={() => onHoverChange(hover?.id === id ? hover : toFeature(hit))}
133
+ onPointerLeave={() => onHoverChange(undefined)}
134
+ >
135
+ <HitComponent
136
+ hit={hit}
137
+ isHovered={hover?.id === parseInt(hit?.record_id, 10)}
138
+ onClick={() => onClick(hit)}
139
+ />
140
+ </div>
141
+ );
142
+ };
143
+
144
+ return (
145
+ <AutoSizer>
146
+ {({ height, width }) => (
147
+ <FixedSizeList
148
+ height={height}
149
+ itemCount={hits.length}
150
+ width={width}
151
+ itemSize={88}
152
+ >
153
+ {Row}
154
+ </FixedSizeList>
155
+ )}
156
+ </AutoSizer>
157
+ );
158
+ };
159
+
160
+ export default SearchResultsList;
@@ -0,0 +1,26 @@
1
+ // @flow
2
+
3
+ import i18next from 'i18next';
4
+
5
+ import en from './en.json';
6
+
7
+ const resources = {
8
+ en: {
9
+ translation: en
10
+ }
11
+ };
12
+
13
+ const i18n = i18next.createInstance();
14
+
15
+ i18n
16
+ .init({
17
+ debug: true,
18
+ fallbackLng: 'en',
19
+ lng: 'en',
20
+ interpolation: {
21
+ escapeValue: false,
22
+ },
23
+ resources
24
+ });
25
+
26
+ export default i18n;
@@ -1,10 +1,14 @@
1
1
  // @flow
2
2
 
3
+ // CSS
4
+ import './index.css';
5
+
3
6
  // Components
4
7
  export { default as LoadAnimation } from './components/LoadAnimation';
5
8
  export { default as MediaGallery } from './components/MediaGallery';
6
9
  export { default as PlaceDetailsPanel } from './components/PlaceDetailsPanel';
7
10
  export { default as PlaceMarker } from './components/PlaceMarker';
11
+ export { default as PlaceResultsList } from './components/PlaceResultsList';
8
12
  export { default as RelatedItemsList } from './components/RelatedItemsList';
9
13
  export { default as RelatedList } from './components/RelatedList';
10
14
  export { default as RelatedMedia } from './components/RelatedMedia';
@@ -0,0 +1,10 @@
1
+ // @flow
2
+
3
+ import type { FeatureGeometry } from './FeatureGeometry';
4
+
5
+ export type Feature = {
6
+ id: number,
7
+ type: 'Feature',
8
+ properties: any,
9
+ geometry: FeatureGeometry
10
+ };
@@ -0,0 +1,14 @@
1
+ // @flow
2
+
3
+ export type Place = {
4
+ uuid: string;
5
+ record_id: string;
6
+ type: string;
7
+ name: string;
8
+ names: string[];
9
+ coordinates: number[];
10
+ geometry: {
11
+ type: 'Point' | 'GeometryCollection',
12
+ coordinates: [ number, number ];
13
+ }
14
+ };
package/webpack.config.js CHANGED
@@ -1,3 +1,33 @@
1
1
  const { configure } = require('@performant-software/webpack-config');
2
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3
+ const path = require('path');
2
4
 
3
- module.exports = configure(__dirname);
5
+ const options = {
6
+ module: {
7
+ rules: [{
8
+ test: /\.(c|le)ss$/,
9
+ use: [
10
+ MiniCssExtractPlugin.loader,
11
+ 'css-loader',
12
+ 'postcss-loader'
13
+ ],
14
+ include: [
15
+ path.resolve(__dirname)
16
+ ]
17
+ }]
18
+ }
19
+ };
20
+
21
+ const mergeOptions = {
22
+ module: {
23
+ rules: {
24
+ test: /\.(c|le)ss$/,
25
+ use: {
26
+ loader: 'match',
27
+ options: 'replace',
28
+ },
29
+ },
30
+ },
31
+ };
32
+
33
+ module.exports = configure(__dirname, options, mergeOptions);