Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.

Commit 08e32ed

Browse files
authored
fix: observer crash (#213)
* fix resize observer crash * make list item popups better
1 parent 98ff1aa commit 08e32ed

File tree

8 files changed

+115
-106
lines changed

8 files changed

+115
-106
lines changed

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"oidc-client": "^1.7.0",
3939
"prom-client": "^11.2.1",
4040
"query-string": "^6.3.0",
41-
"react": "^16.8.4",
41+
"react": "^16.8.6",
4242
"react-codemirror2": "^5.1.0",
4343
"react-dom": "^16.8.4",
4444
"react-helmet": "^5.2.0",

src/shared/components/Animations/InfiniteScroll.tsx

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -31,49 +31,15 @@ export const InfiniteScrollLoadMoreButton: React.FunctionComponent<{
3131
totalItemsListLength: number;
3232
onClick: VoidFunction;
3333
isFetching: boolean;
34-
}> = ({ hasMore, totalItemsListLength, onClick, isFetching }) => {
35-
const animatedOpacity = useSpring({
36-
opacity: hasMore ? 1 : 0,
37-
// TODO why: This doesn't work as described https://www.react-spring.io/docs/hooks/api
38-
config: config.molasses,
39-
delay: DEFAULT_TRAIL_MS * totalItemsListLength,
40-
});
41-
const animatedLoadTransition = useTransition(isFetching, null, {
42-
from: { position: 'absolute', opacity: 0 },
43-
enter: { opacity: 1 },
44-
leave: { opacity: 0 },
45-
});
34+
}> = ({ onClick, isFetching }) => {
4635
return (
47-
<animated.div
48-
style={animatedOpacity}
49-
key="infinite-scroll-load-more-button"
50-
>
51-
<a onClick={onClick}>
52-
<li className="list-item -action -load">
53-
<div className="loading">
54-
{animatedLoadTransition.map(({ item, props }) =>
55-
item ? (
56-
<animated.div
57-
style={props}
58-
className="center-helper"
59-
key="load-more-loading"
60-
>
61-
<div className="center">Loading</div>
62-
</animated.div>
63-
) : (
64-
<animated.div
65-
style={props}
66-
className="center-helper"
67-
key="load-more"
68-
>
69-
<div className="center">Load More</div>
70-
</animated.div>
71-
)
72-
)}
73-
</div>
74-
</li>
75-
</a>
76-
</animated.div>
36+
<a onClick={onClick}>
37+
<li className="list-item -action -load">
38+
<div className="loading">
39+
<div className="center">{isFetching ? 'Loading' : 'Load more'}</div>
40+
</div>
41+
</li>
42+
</a>
7743
);
7844
};
7945

@@ -165,11 +131,6 @@ const InfiniteScroll: React.FunctionComponent<InfiniteScrollProps> = props => {
165131
onClick={loadNextPage}
166132
/>
167133
)}
168-
{!!data && !!data.total && !isFetching && !hasMore && (
169-
<li className="list-item -action -end-of-list">
170-
You've reached the end of this list
171-
</li>
172-
)}
173134
</ul>
174135
)}
175136
</div>

src/shared/components/Animations/ListItem.tsx

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import * as React from 'react';
22
import './list-item.less';
33
import useMeasure from '../hooks/useMeasure';
4+
import { Popover } from 'antd';
5+
import { PopoverProps } from 'antd/lib/popover';
46

57
export interface ListItemProps {
68
label: React.ReactComponentElement<any> | string;
@@ -10,6 +12,7 @@ export interface ListItemProps {
1012
avatar?: { src: string } | null;
1113
action?: React.ReactComponentElement<any> | null;
1214
onClick?: (id: string, event: React.MouseEvent) => void;
15+
popover?: PopoverProps;
1316
}
1417

1518
const ListItem: React.FunctionComponent<ListItemProps> = ({
@@ -20,38 +23,40 @@ const ListItem: React.FunctionComponent<ListItemProps> = ({
2023
details,
2124
action,
2225
avatar,
26+
popover,
2327
}) => {
24-
const [bind, bounds] = useMeasure();
25-
const avatarClassSwitch = bounds && bounds.height > 100 ? `-big` : `-small`;
28+
const ContentWrapper: React.FunctionComponent = popover
29+
? ({ children }) => <Popover {...popover}>{children}</Popover>
30+
: ({ children }) => <div className="wrapper">{children}</div>;
2631
return (
27-
<li
28-
className={`list-item -compact ${avatar ? avatarClassSwitch : ''}`}
29-
tabIndex={1}
30-
onClick={
31-
onClick
32-
? (e: React.MouseEvent<HTMLLIElement, MouseEvent>) => onClick(id, e)
33-
: undefined
34-
}
35-
key={id}
36-
>
37-
{avatar && (
38-
// @ts-ignore can't bothered to figure out which HTMLELEMENT type I need for this
39-
<div {...bind} className={`avatar ${avatarClassSwitch}`}>
40-
<div
41-
className="wrapper"
42-
style={{ backgroundImage: `url(${avatar.src})` }}
43-
>
44-
<img src={avatar.src} />
32+
<ContentWrapper key={id}>
33+
<li
34+
className={`list-item -compact ${avatar ? '-big' : ''}`}
35+
tabIndex={1}
36+
onClick={
37+
onClick
38+
? (e: React.MouseEvent<HTMLLIElement, MouseEvent>) => onClick(id, e)
39+
: undefined
40+
}
41+
>
42+
{avatar && (
43+
<div className={`avatar -big`}>
44+
<div
45+
className="wrapper"
46+
style={{ backgroundImage: `url(${avatar.src})` }}
47+
>
48+
<img src={avatar.src} />
49+
</div>
4550
</div>
51+
)}
52+
<div className="content">
53+
<span className="label">{label}</span>
54+
{details && <div className="details">{details}</div>}
55+
{action && <div className="actions">{action}</div>}
4656
</div>
47-
)}
48-
<div className="content">
49-
<span className="label">{label}</span>
50-
{details && <div className="details">{details}</div>}
51-
{action && <div className="actions">{action}</div>}
52-
</div>
53-
{description && <p className="description">{description}</p>}
54-
</li>
57+
{description && <p className="description">{description}</p>}
58+
</li>
59+
</ContentWrapper>
5560
);
5661
};
5762

src/shared/components/Workspace/Queries/Query/QueryComponent.tsx

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import RenameableItem from '../../../Renameable';
1212
import FullTextSearch from './Search';
1313
import TypesFilter from './Types';
1414
import SchemasFilter from './Schemas';
15+
import QueryListItem from './QueryItem';
1516

1617
const MOUSE_ENTER_DELAY = 0.5;
1718

@@ -152,27 +153,10 @@ const QueryComponent: React.FunctionComponent<QueryComponentProps> = props => {
152153
}
153154
return (
154155
<div key={resource.id}>
155-
<ListItem
156+
<QueryListItem
157+
getFilePreview={getFilePreview}
156158
onClick={handleOnClick(resource)}
157-
label={
158-
<Popover
159-
content={
160-
<ResourceMetadataCard
161-
{...{ ...resource, name: resource.name }}
162-
/>
163-
}
164-
mouseEnterDelay={MOUSE_ENTER_DELAY}
165-
key={resource.id}
166-
>
167-
{resource.name}
168-
</Popover>
169-
}
170-
id={resource.id}
171-
details={
172-
resource.type && !!resource.type.length ? (
173-
<TypesIconList type={resource.type} />
174-
) : null
175-
}
159+
resource={resource}
176160
/>
177161
</div>
178162
);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as React from 'react';
2+
import { Popover } from 'antd';
3+
import { Resource, NexusFile } from '@bbp/nexus-sdk';
4+
import ListItem from '../../../Animations/ListItem';
5+
import ResourceMetadataCard from '../../../Resources/MetadataCard';
6+
import TypesIconList from '../../../Types/TypesIcon';
7+
import { hasDisplayableImage } from '../../../Resources/ResourcePreview';
8+
import useNexusFile from '../../../hooks/useNexusFile';
9+
10+
const MOUSE_ENTER_DELAY = 0.5;
11+
12+
export interface QueryListItemProps {
13+
resource: Resource;
14+
onClick?(resource: Resource): void;
15+
onEdit?(): void;
16+
getFilePreview: (selfUrl: string) => Promise<NexusFile>;
17+
}
18+
19+
const QueryListItem: React.FunctionComponent<QueryListItemProps> = props => {
20+
const { resource, getFilePreview, onClick = () => {} } = props;
21+
const file = useNexusFile(resource, hasDisplayableImage, getFilePreview);
22+
let avatar = null;
23+
if (file) {
24+
const img = new Image();
25+
img.src = `data:${file.mediaType};base64,${file.rawFile as string}`;
26+
avatar = file && {
27+
src: img.src,
28+
};
29+
}
30+
return (
31+
<ListItem
32+
popover={{
33+
content: (
34+
<ResourceMetadataCard {...{ ...resource, name: resource.name }} />
35+
),
36+
mouseEnterDelay: MOUSE_ENTER_DELAY,
37+
key: resource.id,
38+
}}
39+
avatar={avatar}
40+
onClick={() => onClick(resource)}
41+
label={resource.name}
42+
id={resource.id}
43+
details={
44+
resource.type && !!resource.type.length ? (
45+
<TypesIconList type={resource.type} />
46+
) : null
47+
}
48+
/>
49+
);
50+
};
51+
52+
export default QueryListItem;

src/shared/components/Workspace/Queries/Query/query-component.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
}
2424
& > .ant-spin-nested-loading > .ant-spin-container > .infinite-scroll {
2525
height: 100%;
26+
display: content;
2627
overflow: hidden;
2728
overflow-y: auto;
2829
}

src/shared/components/hooks/useMeasure.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,22 @@ export default function useMeasure() {
1717
height: 0,
1818
});
1919
const [ro] = React.useState(
20-
() => new ResizeObserver(([entry]) => set(entry.contentRect))
20+
new ResizeObserver(([entry]) => set(entry.contentRect))
2121
);
2222
React.useEffect(() => {
2323
if (ref && ref.current) {
2424
ro.observe(ref.current);
2525
const { height, width, top, left } = ref.current.getBoundingClientRect();
2626
set({ height, width, top, left });
2727
}
28-
return ro.disconnect;
29-
}, [ref]);
28+
return () => {
29+
if (ref && ref.current) {
30+
ro.unobserve(ref.current);
31+
}
32+
ro.disconnect();
33+
};
34+
}, [ref, ro]);
35+
3036
return [{ ref }, bounds] as [
3137
{ ref: React.MutableRefObject<HTMLElement> },
3238
Bounds

0 commit comments

Comments
 (0)