Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use GitHub native popover to substitue ours #795

Merged
merged 11 commits into from
Apr 29, 2024
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"react-dom": "^17.0.2",
"react-hot-loader": "^4.13.0",
"react-modal": "3.15.1",
"react-tooltip": "^4.2.21",
"strip-indent": "^4.0.0"
},
"devDependencies": {
Expand Down
90 changes: 90 additions & 0 deletions src/pages/ContentScripts/components/NativePopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { PropsWithChildren, useEffect } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import elementReady from 'element-ready';
import $ from 'jquery';

interface NativePopoverProps extends PropsWithChildren<any> {
anchor: JQuery<HTMLElement>;
width: number;
// for now, only support top-middle
arrowPosition:
| 'top-left'
| 'top-middle'
| 'top-right'
| 'bottom-left'
| 'bottom-middle'
| 'bottom-right';
}

export const NativePopover = ({
anchor,
width,
arrowPosition,
children,
}: NativePopoverProps): JSX.Element => {
useEffect(() => {
(async () => {
await elementReady('div.Popover');
await elementReady('div.Popover-message');
const $popoverContainer = $('div.Popover');
const $popoverContent = $('div.Popover-message');
let popoverTimer: NodeJS.Timeout | null = null;
let leaveTimer: NodeJS.Timeout | null = null;

const showPopover = () => {
popoverTimer = setTimeout(() => {
const anchorOffset = anchor.offset();
const anchorWidth = anchor.outerWidth();
const anchorHeight = anchor.outerHeight();
if (!anchorOffset || !anchorHeight || !anchorWidth) {
return;
}
const { top, left } = anchorOffset;

$popoverContent.css('padding', '10px 5px');
$popoverContent.css('width', width);
$popoverContainer.css('top', `${top + anchorHeight + 10}px`);
$popoverContainer.css(
'left',
`${left - (width - anchorWidth) / 2}px`
);
$popoverContent.attr(
'class',
`Popover-message Box color-shadow-large Popover-message--${arrowPosition}`
);
render(children, $popoverContent[0]);
$popoverContainer.css('display', 'block');
}, 1000);
};

const hidePopover = () => {
popoverTimer && clearTimeout(popoverTimer);
$popoverContent.addClass('Popover-message--large');
if ($popoverContent.children().length > 0) {
unmountComponentAtNode($popoverContent[0]);
}
$popoverContainer.css('display', 'none');
};

anchor[0].addEventListener('mouseenter', () => {
popoverTimer = null;
leaveTimer && clearTimeout(leaveTimer);
showPopover();
});

anchor[0].addEventListener('mouseleave', () => {
leaveTimer = setTimeout(hidePopover, 200);
});

$popoverContainer[0].addEventListener('mouseenter', () => {
leaveTimer && clearTimeout(leaveTimer);
});

$popoverContainer[0].addEventListener('mouseleave', () => {
leaveTimer = setTimeout(hidePopover, 200);
});
})();
}, []);

return <></>;
};
62 changes: 20 additions & 42 deletions src/pages/ContentScripts/features/repo-fork-tooltip/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import React from 'react';
import { render, Container } from 'react-dom';
import elementReady from 'element-ready';
import $ from 'jquery';

import features from '../../../../feature-manager';
import getGithubTheme from '../../../../helpers/get-github-theme';
import View from './view';
import { NativePopover } from '../../components/NativePopover';
import elementReady from 'element-ready';
import {
getRepoName,
hasRepoContainerHeader,
isPublicRepoWithMeta,
} from '../../../../helpers/get-repo-info';
import { getForks } from '../../../../api/repo';
import { RepoMeta, metaStore } from '../../../../api/common';
import View from './view';

const githubTheme = getGithubTheme();
import React from 'react';
import { render } from 'react-dom';
import $ from 'jquery';

const featureId = features.getFeatureID(import.meta.url);
let repoName: string;
let forks: any;
Expand All @@ -25,46 +24,25 @@ const getData = async () => {
meta = (await metaStore.get(repoName)) as RepoMeta;
};

const renderTo = (container: Container) => {
render(<View forks={forks} meta={meta} />, container);
};

const init = async (): Promise<void> => {
repoName = getRepoName();
await getData();

const selector = '#fork-button';
await elementReady(selector);
$(selector).attr({
'data-tip': '',
'data-for': 'fork-tooltip',
'data-class': `floating-window ${githubTheme}`,
'data-place': 'left',
'data-effect': 'solid',
'data-delay-hide': 500,
'data-delay-show': 1000,
style: { color: githubTheme === 'light' ? '#24292f' : '#c9d1d9' },
'data-text-color': githubTheme === 'light' ? '#24292F' : '#C9D1D9',
'data-background-color': githubTheme === 'light' ? 'white' : '#161B22',
});
const container = document.createElement('div');
container.id = featureId;
renderTo(container);
(await elementReady('#repository-container-header'))?.append(container);
const forkButtonSelector = '#fork-button';
await elementReady(forkButtonSelector);
const $forkButton = $(forkButtonSelector);
const placeholderElement = $('<div class="NativePopover" />').appendTo(
'body'
)[0];
render(
<NativePopover anchor={$forkButton} width={280} arrowPosition="top-middle">
<View forks={forks} meta={meta} />
</NativePopover>,
placeholderElement
);
};

const restore = async () => {
// Clicking another repo link in one repo will trigger a turbo:visit,
// so in a restoration visit we should be careful of the current repo.
if (repoName !== getRepoName()) {
repoName = getRepoName();
await getData();
}
// Ideally, we should do nothing if the container already exists. But after a tubor
// restoration visit, tooltip cannot be triggered though it exists in DOM tree. One
// way to solve this is to rerender the view to the container. At least this way works.
renderTo($(`#${featureId}`)[0]);
};
const restore = async () => {};

features.add(featureId, {
asLongAs: [isPublicRepoWithMeta, hasRepoContainerHeader],
Expand Down
5 changes: 2 additions & 3 deletions src/pages/ContentScripts/features/repo-fork-tooltip/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import optionsStorage, {
defaults,
} from '../../../../options-storage';
import generateDataByMonth from '../../../../helpers/generate-data-by-month';
import ReactTooltip from 'react-tooltip';
import ForkChart from './ForkChart';
import { RepoMeta } from '../../../../api/common';

Expand All @@ -30,7 +29,7 @@ const View = ({ forks, meta }: Props): JSX.Element | null => {
if (!forks) return null;

return (
<ReactTooltip id="fork-tooltip" clickable={true}>
<>
<div className="chart-title">
{getMessageByLocale('fork_popup_title', options.locale)}
</div>
Expand All @@ -40,7 +39,7 @@ const View = ({ forks, meta }: Props): JSX.Element | null => {
height={130}
data={generateDataByMonth(forks, meta.updatedAt)}
/>
</ReactTooltip>
</>
);
};

Expand Down
12 changes: 6 additions & 6 deletions src/pages/ContentScripts/features/repo-header-labels/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import React from 'react';
import { render, Container } from 'react-dom';
import elementReady from 'element-ready';
import $ from 'jquery';

import features from '../../../../feature-manager';
import View from './view';
import elementReady from 'element-ready';
import {
getRepoName,
hasRepoContainerHeader,
Expand All @@ -16,7 +13,10 @@ import {
getContributor,
} from '../../../../api/repo';
import { RepoMeta, metaStore } from '../../../../api/common';
import View from './view';

import React from 'react';
import { render, Container } from 'react-dom';
import $ from 'jquery';

const featureId = features.getFeatureID(import.meta.url);
let repoName: string;
Expand Down
123 changes: 70 additions & 53 deletions src/pages/ContentScripts/features/repo-header-labels/view.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import React, { useState, useEffect } from 'react';

import getGithubTheme from '../../../../helpers/get-github-theme';
import getMessageByLocale from '../../../../helpers/get-message-by-locale';
import { isNull } from '../../../../helpers/is-null';
import { numberWithCommas } from '../../../../helpers/formatter';
import { NativePopover } from '../../components/NativePopover';
import optionsStorage, {
HypercrxOptions,
defaults,
} from '../../../../options-storage';
import { rocketLight, rocketDark } from './base64';
import ReactTooltip from 'react-tooltip';
import generateDataByMonth from '../../../../helpers/generate-data-by-month';
import ActivityChart from './ActivityChart';
import OpenRankChart from './OpenRankChart';
import ParticipantChart from './ParticipantChart';
import ContributorChart from './ContributorChart';
import { RepoMeta } from '../../../../api/common';

import React, { useState, useEffect } from 'react';
import { render } from 'react-dom';
import $ from 'jquery';

const githubTheme = getGithubTheme();

interface Props {
Expand All @@ -36,16 +38,77 @@ const View = ({
}: Props): JSX.Element | null => {
const [options, setOptions] = useState<HypercrxOptions>(defaults);

useEffect(() => {
ReactTooltip.rebuild();
}, []);

useEffect(() => {
(async function () {
setOptions(await optionsStorage.getAll());
})();
}, []);

useEffect(() => {
const placeholderElement = $('<div class="NativePopover" />').appendTo(
'body'
)[0];
render(
<>
<NativePopover
anchor={$('#activity-header-label')}
width={280}
arrowPosition="top-middle"
>
<div className="chart-title">
{getMessageByLocale('header_label_activity', options.locale)}
</div>
<ActivityChart
theme={githubTheme as 'light' | 'dark'}
width={270}
height={130}
data={activityData}
/>
</NativePopover>
<NativePopover
anchor={$('#OpenRank-header-label')}
width={280}
arrowPosition="top-middle"
>
<div className="chart-title">
{getMessageByLocale('header_label_OpenRank', options.locale)}
</div>
<OpenRankChart
theme={githubTheme as 'light' | 'dark'}
width={270}
height={130}
data={openrankData}
/>
</NativePopover>
<NativePopover
anchor={$('#participant-header-label')}
width={280}
arrowPosition="top-middle"
>
<div className="chart-title">
{getMessageByLocale('header_label_contributor', options.locale)}
</div>
<ContributorChart
theme={githubTheme as 'light' | 'dark'}
width={270}
height={130}
data={contributorData}
/>
<div className="chart-title">
{getMessageByLocale('header_label_participant', options.locale)}
</div>
<ParticipantChart
theme={githubTheme as 'light' | 'dark'}
width={270}
height={130}
data={participantData}
/>
</NativePopover>
</>,
placeholderElement
);
}, []);

if (
isNull(activity) ||
isNull(openrank) ||
Expand Down Expand Up @@ -147,52 +210,6 @@ const View = ({
{numberWithCommas(contributorData[contributorData.length - 1][1])}/
{numberWithCommas(participantData[participantData.length - 1][1])}
</span>
<ReactTooltip
id="activity-tooltip"
className={githubTheme === 'dark' ? 'custom-react-tooltip' : ''}
clickable={true}
>
<div className="chart-title">
{getMessageByLocale('header_label_activity', options.locale)}
</div>
<ActivityChart
theme={githubTheme as 'light' | 'dark'}
width={270}
height={130}
data={activityData}
/>
</ReactTooltip>
<ReactTooltip id="openrank-tooltip" clickable={true}>
<div className="chart-title">
{getMessageByLocale('header_label_OpenRank', options.locale)}
</div>
<OpenRankChart
theme={githubTheme as 'light' | 'dark'}
width={270}
height={130}
data={openrankData}
/>
</ReactTooltip>
<ReactTooltip id="participant-tooltip" clickable={true}>
<div className="chart-title">
{getMessageByLocale('header_label_contributor', options.locale)}
</div>
<ContributorChart
theme={githubTheme as 'light' | 'dark'}
width={270}
height={130}
data={contributorData}
/>
<div className="chart-title">
{getMessageByLocale('header_label_participant', options.locale)}
</div>
<ParticipantChart
theme={githubTheme as 'light' | 'dark'}
width={270}
height={130}
data={participantData}
/>
</ReactTooltip>
</div>
);
};
Expand Down
Loading
Loading