Skip to content

Commit

Permalink
Update search components to functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
cbeer committed Dec 3, 2024
1 parent ee210c9 commit 1164409
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 311 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
"stylis": "^4.3.0",
"stylis-plugin-rtl": "^2.1.1",
"url": "^0.11.0",
"use-debounce": "^10.0.4",
"use-effect-event": "^1.0.2",
"uuid": "^8.1.0 || ^9.0.0 || ^10.0.0 || ^11.0.0"
},
"devDependencies": {
Expand Down
259 changes: 99 additions & 160 deletions src/components/SearchHit.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component } from 'react';
import { useEffect } from 'react';
import { useEffectEvent } from 'use-effect-event';
import PropTypes from 'prop-types';
import Button from '@mui/material/Button';
import ListItem from '@mui/material/ListItem';
Expand Down Expand Up @@ -45,50 +46,27 @@ const Counter = styled(Chip, { name: 'SearchHit', slot: 'counter' })(({ ownerSta
}));

/** */
export class SearchHit extends Component {
/** */
constructor(props) {
super(props);

this.handleClick = this.handleClick.bind(this);
}

/**
* Announce the annotation content if the component is mounted selected
*/
componentDidMount() {
const { selected } = this.props;

if (selected) this.announceHit();
}

/**
* Announce hit if the hit has been selected
*/
componentDidUpdate(prevProps) {
const { selected } = this.props;

if (selected && selected !== prevProps.selected) {
this.announceHit();
export function SearchHit({
adjacent = false, annotation = undefined, annotationId = undefined, annotationLabel = undefined,
announcer = undefined, canvasLabel = undefined, companionWindowId = undefined, containerRef = undefined,
focused = false, hit = undefined, index = undefined, selectAnnotation = () => {}, selected = false,
showDetails = () => {}, t = k => k, total = undefined, windowId, windowSelected = false,
}) {
useEffect(() => {
if (selected) {
announceHit();
}
}
}, [selected]); // eslint-disable-line react-hooks/exhaustive-deps

/** */
handleClick() {
const {
annotation, annotationId, selectAnnotation,
} = this.props;

const handleClick = () => {
if (annotation && annotationId) selectAnnotation(annotationId);
}
};

/**
* Pass content describing the hit to the announcer prop (intended for screen readers)
*/
announceHit() {
const {
annotation, annotationLabel, announcer, canvasLabel, hit, index, t, total,
} = this.props;
const announceHit = useEffectEvent(() => {
if (!hit || !announcer) return;
const truncatedHit = new TruncatedHit(hit, annotation);

Expand All @@ -103,110 +81,91 @@ export class SearchHit extends Component {
].join(' '),
'polite',
);
}

/** */
render() {
const {
adjacent,
annotation,
annotationLabel,
canvasLabel,
companionWindowId,
containerRef,
hit,
focused,
index,
showDetails,
selected,
t,
windowSelected,
} = this.props;

if (focused && !selected) return null;

const truncatedHit = focused ? hit : hit && new TruncatedHit(hit, annotation);
const truncated = hit && (truncatedHit.before !== hit.before || truncatedHit.after !== hit.after);
const canvasLabelHtmlId = `${companionWindowId}-${index}`;
const ownerState = {
adjacent, focused, selected, windowSelected,
};

const header = (
<>
<Counter
component="span"
ownerState={ownerState}
label={index + 1}
/>
<CanvasLabel id={canvasLabelHtmlId}>
{canvasLabel}
{annotationLabel && (
<Typography component="span" sx={{ display: 'block', marginTop: 1 }}>{annotationLabel}</Typography>
)}
</CanvasLabel>
</>
);

return (
<ScrollTo
containerRef={containerRef}
offsetTop={96} // offset for the height of the form above
scrollTo={windowSelected && !focused}
}, [annotation, annotationLabel, announcer, canvasLabel, hit, index, t, total]);

if (focused && !selected) return null;

const truncatedHit = focused ? hit : hit && new TruncatedHit(hit, annotation);
const truncated = hit && (truncatedHit.before !== hit.before || truncatedHit.after !== hit.after);
const canvasLabelHtmlId = `${companionWindowId}-${index}`;
const ownerState = {
adjacent, focused, selected, windowSelected,
};

const header = (
<>
<Counter
component="span"
ownerState={ownerState}
label={index + 1}
/>
<CanvasLabel id={canvasLabelHtmlId}>
{canvasLabel}
{annotationLabel && (
<Typography component="span" sx={{ display: 'block', marginTop: 1 }}>{annotationLabel}</Typography>
)}
</CanvasLabel>
</>
);

return (
<ScrollTo
containerRef={containerRef}
offsetTop={96} // offset for the height of the form above
scrollTo={windowSelected && !focused}
>
<Root
ownerState={ownerState}
className={windowSelected ? 'windowSelected' : ''}
divider
button={!selected}
component="li"
onClick={handleClick}
selected={selected}
>
<Root
ownerState={ownerState}
className={windowSelected ? 'windowSelected' : ''}
divider
button={!selected}
component="li"
onClick={this.handleClick}
selected={selected}
>
<ListItemText
primary={header}
primaryTypographyProps={{ component: 'div', sx: { marginBottom: 1 }, variant: 'subtitle2' }}
secondaryTypographyProps={{ variant: 'body1' }}
secondary={(
<>
{hit && (
<>
<SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.before} />
{' '}
<strong>
<SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.match} />
</strong>
{' '}
<SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.after} />
{' '}
{truncated && !focused && (
<Button
sx={{
'& span': {
lineHeight: '1.5em',
},
margin: 0,
padding: 0,
textTransform: 'none',
}}
onClick={showDetails}
color="secondary"
size="small"
aria-describedby={canvasLabelHtmlId}
>
{t('more')}
</Button>
)}
</>
)}
{!hit && annotation && <SanitizedHtml ruleSet="iiif" htmlString={annotation.chars} />}
</>
)}
/>
</Root>
</ScrollTo>
);
}
<ListItemText
primary={header}
primaryTypographyProps={{ component: 'div', sx: { marginBottom: 1 }, variant: 'subtitle2' }}
secondaryTypographyProps={{ variant: 'body1' }}
secondary={(
<>
{hit && (
<>
<SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.before} />
{' '}
<strong>
<SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.match} />
</strong>
{' '}
<SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.after} />
{' '}
{truncated && !focused && (
<Button
sx={{
'& span': {
lineHeight: '1.5em',
},
margin: 0,
padding: 0,
textTransform: 'none',
}}
onClick={showDetails}
color="secondary"
size="small"
aria-describedby={canvasLabelHtmlId}
>
{t('more')}
</Button>
)}
</>
)}
{!hit && annotation && <SanitizedHtml ruleSet="iiif" htmlString={annotation.chars} />}
</>
)}
/>
</Root>
</ScrollTo>
);
}

SearchHit.propTypes = {
Expand Down Expand Up @@ -239,23 +198,3 @@ SearchHit.propTypes = {
windowId: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types
windowSelected: PropTypes.bool,
};

SearchHit.defaultProps = {
adjacent: false,
annotation: undefined,
annotationId: undefined,
annotationLabel: undefined,
announcer: undefined,
canvasLabel: undefined,
companionWindowId: undefined,
containerRef: undefined,
focused: false,
hit: undefined,
index: undefined,
selectAnnotation: () => {},
selected: false,
showDetails: () => {},
t: k => k,
total: undefined,
windowSelected: false,
};
Loading

0 comments on commit 1164409

Please sign in to comment.