Skip to content

Commit

Permalink
Merge pull request #324 from performant-software/feature/cdp322_models
Browse files Browse the repository at this point in the history
CDP #33 - Core data models
  • Loading branch information
dleadbetter authored Jan 29, 2025
2 parents 4cac214 + 7837317 commit dadca61
Show file tree
Hide file tree
Showing 16 changed files with 288 additions and 111 deletions.
23 changes: 18 additions & 5 deletions packages/core-data/src/components/FacetTimeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,33 @@ type Props = {
onLoad?: (events: Array<EventType>) => void,

/**
* Typesense `useRange` hook.
* The absolute min/max values for the timeline range.
*/
useRange: ({ attribute: string }) => ({ refine: (range: [number, number]) => void }) => void,
range: {
max: number,
min: number
},

/**
* Callback fired when the slider value(s) are changed.
*
* @param any
*/
refine: (number | [number, number]) => void,

/**
* The current value of the slider.
*/
start: [number, number],

/**
* Zoom level increment.
*/
zoom?: number
};

const FACET_EVENT_RANGE = 'event_range_facet';

const FacetTimeline = (props: Props) => {
const { range = {}, refine, start = [] } = props.useRange({ attribute: FACET_EVENT_RANGE });
const { range = {}, refine, start = [] } = props;

const from = Math.max(range.min, Number.isFinite(start[0]) ? start[0] : range.min);
const to = Math.min(range.max, Number.isFinite(start[1]) ? start[1] : range.max);
Expand Down
35 changes: 35 additions & 0 deletions packages/core-data/src/components/HeaderImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @flow

import React from 'react';
import { Image } from 'lucide-react';

type Props = {
alt?: string,
src: string
};

const HeaderImage = (props: Props) => (
<div
className='relative w-full h-[200px] flex-grow-0 flex-shrink-0 bg-muted/20 z-0'
>
<div
className='absolute top-0 left-0 w-full h-full flex justify-center items-center'
>
<Image
className='h-20 w-20 text-gray-400'
strokeWidth={1}
/>
</div>
<div
className='absolute top-0 left-0 w-full h-full flex justify-center items-center'
>
<img
alt={props.alt}
className='object-cover h-full w-full'
src={props.src}
/>
</div>
</div>
);

export default HeaderImage;
39 changes: 39 additions & 0 deletions packages/core-data/src/components/KeyValueList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// @flow

import React from 'react';
import _ from 'underscore';

type Item = {
label: string,
value: string
};

type Props = {
items: Array<Item>
};

const KeyValueList = (props: Props) => (
<ol
className='text-sm mt-4 leading-6 overflow-hidden'
>
{ _.map(props.items, ({ label, value }) => (
<li
key={label}
className='mb-2'
>
<div
className='text-muted'
>
{ label }
</div>
<div
className='font-medium overflow-hidden text-ellipsis'
>
{ value }
</div>
</li>
))}
</ol>
);

export default KeyValueList;
59 changes: 15 additions & 44 deletions packages/core-data/src/components/PlaceDetails.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// @flow

import { Image } from 'lucide-react';
import React, { useCallback, useEffect, useMemo } from 'react';
import _ from 'underscore';
import HeaderImage from './HeaderImage';
import KeyValueList from './KeyValueList';
import { useLoader, usePlacesService } from '../hooks/CoreData';

type Props = {
Expand All @@ -20,7 +21,10 @@ type Props = {
};

/**
* This component renders a detail view for the passed Core Data place record.
* This component renders a detail view for the passed Core Data place record. This component is deprecated, as it is
* just a composition of other components, and will be removed in a future release.
*
* @deprecated
*/
const PlaceDetails = (props: Props) => {
const PlacesService = usePlacesService();
Expand Down Expand Up @@ -71,27 +75,10 @@ const PlaceDetails = (props: Props) => {
return (
<>
{ image && (
<div
className='relative w-full h-[200px] flex-grow-0 flex-shrink-0 bg-muted/20 z-0'
>
<div
className='absolute top-0 left-0 w-full h-full flex justify-center items-center'
>
<Image
className='h-20 w-20 text-gray-400'
strokeWidth={1}
/>
</div>
<div
className='absolute top-0 left-0 w-full h-full flex justify-center items-center'
>
<img
className='object-cover h-full w-full'
src={image.content_iiif_url}
alt={image.name}
/>
</div>
</div>
<HeaderImage
alt={image.name}
src={image.content_iiif_url}
/>
)}
{ place && (
<div
Expand All @@ -102,27 +89,11 @@ const PlaceDetails = (props: Props) => {
>
{ place.name }
</h1>
<ol
className='text-sm mt-4 leading-6 overflow-hidden'
>
{ _.map(userDefined, ({ label, value }) => (
<li
key={label}
className='mb-2'
>
<div
className='text-muted'
>
{ label }
</div>
<div
className='font-medium overflow-hidden text-ellipsis'
>
{ value }
</div>
</li>
))}
</ol>
{ userDefined && (
<KeyValueList
items={userDefined}
/>
)}
</div>
)}
</>
Expand Down
12 changes: 5 additions & 7 deletions packages/core-data/src/components/SearchResultsLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@ type Props = {
fitBoundingBox?: boolean,

/**
* If `true`, polygons will be shown (when available) on the map
* instead of points.
* Path to the geometry attribute for each result.
*/
showPolygons?: boolean
geometry: string,
}

/**
Expand All @@ -50,10 +49,9 @@ const SearchResultsLayer = (props: Props) => {
*
* @type {unknown}
*/
const data = useMemo(
() => !_.isEmpty(hits) && TypesenseUtils.toFeatureCollection(hits, props.showPolygons),
[hits, props.showPolygons]
);
const data = useMemo(() => (
!_.isEmpty(hits) && TypesenseUtils.toFeatureCollection(hits, props.geometry)
), [hits, props.geometry]);

/**
* Here we'll implement our own fitting of the bounding box once the search has completed and the map has loaded,
Expand Down
11 changes: 11 additions & 0 deletions packages/core-data/src/hooks/CoreData.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import CoreDataContext from '../context/CoreData';
import EventsService from '../services/Events';
import InstancesService from '../services/Instances';
import ItemsService from '../services/Items';
import OrganizationsService from '../services/Organizations';
import PeopleService from '../services/People';
import PlacesService from '../services/Places';
import WorksService from '../services/Works';
Expand Down Expand Up @@ -132,6 +133,16 @@ export const useItemsService = () => {
return new ItemsService(baseUrl, projectIds);
};

/**
* Hook to initialize the organizations service.
*
* @returns {Items}
*/
export const useOrganizationsService = () => {
const { baseUrl, projectIds } = useContext(CoreDataContext);
return new OrganizationsService(baseUrl, projectIds);
};

/**
* Hook to initialize the people service.
*
Expand Down
23 changes: 20 additions & 3 deletions packages/core-data/src/hooks/ProgressiveSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import TypesenseUtils from '../utils/Typesense';

type OnCompleteCallback = (results: Array<SearchResult>) => void;

const useProgressiveSearch = (infiniteHits) => {
const useProgressiveSearch = (infiniteHits, transformResults = null) => {
const [cachedHits, setCachedHits] = useState(TypesenseUtils.createCachedHits([]));

const lastSearchState = useRef<any>();
Expand Down Expand Up @@ -59,11 +59,28 @@ const useProgressiveSearch = (infiniteHits) => {
return !dequal(a, b);
};

/**
* Returns the transformed hits if the callback is provided. Otherwise, the untransformed hits are returned.
*
* @param results
*
* @returns {*}
*/
const getHits = (results) => {
let value = results.hits;

if (transformResults) {
value = transformResults(value);
}

return value;
};

useEffect(() => {
const { results } = infiniteHits;

const isFirstPage = results.page === 0;
const hits = TypesenseUtils.normalizeResults(results.hits);
const hits = getHits(results);

// Add to cache and load next page
if (isFirstPage && hasStateChanged(results._state, lastSearchState.current, true)) {
Expand All @@ -75,8 +92,8 @@ const useProgressiveSearch = (infiniteHits) => {

useEffect(() => {
const { results } = infiniteHits;
const hits = getHits(results);

const hits = TypesenseUtils.normalizeResults(results.hits);
const isLastPage = results.page + 1 >= results.nbPages;

if (!isLastPage && infiniteHits.showMore) {
Expand Down
4 changes: 4 additions & 0 deletions packages/core-data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export { default as FacetListsGrouped } from './components/FacetListsGrouped';
export { default as FacetSlider } from './components/FacetSlider';
export { default as FacetStateContextProvider } from './components/FacetStateContextProvider';
export { default as FacetTimeline } from './components/FacetTimeline';
export { default as HeaderImage } from './components/HeaderImage';
export { default as KeyValueList } from './components/KeyValueList';
export { default as LayerMenu } from './components/LayerMenu';
export { default as LoadAnimation } from './components/LoadAnimation';
export { default as MediaGallery } from './components/MediaGallery';
Expand Down Expand Up @@ -49,6 +51,7 @@ export {
useInstancesService,
useItemsService,
useLoader,
useOrganizationsService,
usePeopleService,
usePlacesService,
useWorksService
Expand All @@ -66,6 +69,7 @@ export {
export { default as EventsService } from './services/Events';
export { default as InstancesService } from './services/Instances';
export { default as ItemsService } from './services/Items';
export { default as OrganizationsService } from './services/Organizations';
export { default as PeopleService } from './services/People';
export { default as PlacesService } from './services/Places';
export { default as WorksService } from './services/Works';
Expand Down
19 changes: 19 additions & 0 deletions packages/core-data/src/services/Organizations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @flow

import BaseService from './BaseService';

/**
* Class responsible for making API calls to the Core Data `organizations` API endpoint.
*/
class Organizations extends BaseService {
/**
* Returns the organizations route name.
*
* @returns {string}
*/
getRoute() {
return 'organizations';
}
}

export default Organizations;
Loading

0 comments on commit dadca61

Please sign in to comment.