diff --git a/package.json b/package.json
index 3ab0ef381..551d7d8b7 100644
--- a/package.json
+++ b/package.json
@@ -59,7 +59,6 @@
"pdfkit": "^0.12.1",
"preact": "^10.5.5",
"preact-i18n": "^2.3.0-preactx",
- "prop-types": "^15.7.2",
"qrcode": "^1.4.4",
"react-instantsearch-dom": "^6.26.0",
"react-scrollspy": "^3.4.0",
@@ -85,6 +84,7 @@
"@types/preact-i18n": "^2.3.0-preactx",
"@types/qrcode": "^1.4.2",
"@types/react-instantsearch-dom": "^6.12.3",
+ "@types/react-scrollspy": "^3.3.9",
"@typescript-eslint/eslint-plugin": "^5.15.0",
"@typescript-eslint/parser": "^5.15.0",
"babel-loader": "^8.0.6",
diff --git a/src/Components/DeprecatedModal.js b/src/Components/DeprecatedModal.js
index 81a7207b4..ac45828d4 100644
--- a/src/Components/DeprecatedModal.js
+++ b/src/Components/DeprecatedModal.js
@@ -1,7 +1,6 @@
import { render, Component } from 'preact';
import { createPortal } from 'preact/compat';
import t from '../Utility/i18n';
-import PropTypes from 'prop-types';
// TODO: Get rid of this once we've moved everything to the new modal hook.
export default class DeprecatedModal extends Component {
@@ -63,22 +62,6 @@ export default class DeprecatedModal extends Component {
);
/* eslint-enable */
}
-
- static propTypes = {
- positiveButton: PropTypes.element,
- positiveText: PropTypes.string,
- positiveDefault: PropTypes.bool,
- onPositiveFeedback: PropTypes.func,
-
- negativeButton: PropTypes.element,
- negativeText: PropTypes.string,
- onNegativeFeedback: PropTypes.func,
-
- onDismiss: PropTypes.func,
- innerStyle: PropTypes.string,
-
- children: PropTypes.node.isRequired,
- };
}
export function showModal(modal) {
diff --git a/src/company-list.js b/src/company-list.js
deleted file mode 100644
index 5d864abc8..000000000
--- a/src/company-list.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import { render, Component } from 'preact';
-import { SearchBar } from 'Components/SearchBar';
-import { IntlProvider, Text } from 'preact-i18n';
-import PropTypes from 'prop-types';
-
-import t from 'Utility/i18n';
-import { Privacy, PRIVACY_ACTIONS } from 'Utility/Privacy';
-import Scrollspy from 'react-scrollspy';
-
-if (!Privacy.isAllowed(PRIVACY_ACTIONS.SEARCH) && document.getElementById('aa-search-input'))
- document.getElementById('aa-search-input').style.display = 'none';
-
-class CompanyList extends Component {
- render() {
- let anchor_map = new Map([
- ['#', 'numbers'],
- ['A', 'A'],
- ['B', 'B'],
- ['C', 'C'],
- ['D', 'D'],
- ['E', 'E'],
- ['F', 'F'],
- ['G', 'G'],
- ['H', 'H'],
- ['I', 'I'],
- ['J', 'J'],
- ['K', 'K'],
- ['L', 'L'],
- ['M', 'M'],
- ['N', 'N'],
- ['O', 'O'],
- ['P', 'P'],
- ['Q', 'Q'],
- ['R', 'R'],
- ['S', 'S'],
- ['T', 'T'],
- ['U', 'U'],
- ['V', 'V'],
- ['W', 'W'],
- ['X', 'X'],
- ['Y', 'Y'],
- ['Z', 'Z'],
- ]);
- let anchor_links = [];
- let anchor_ids = [];
- anchor_map.forEach((value, key) => {
- anchor_links.push(
-
-
- {key}
-
-
- );
- anchor_ids.push(value + '-container');
- });
-
- return (
-
-
-
-
-
-
-
-
-
-
- {anchor_links}
-
-
-
-
-
- );
- }
-
- componentDidMount() {
- window.onscroll = () => {
- let controls = document.getElementById('company-list-controls');
- if (controls) {
- if (window.pageYOffset > controls.offsetTop) {
- controls.classList.add('sticky');
- document.body.classList.add('sticky-offset');
- }
- if (window.pageYOffset < controls.offsetTop + 200) {
- controls.classList.remove('sticky');
- document.body.classList.remove('sticky-offset');
- }
- }
- };
- }
-}
-
-class CompanySearch extends Component {
- render() {
- return (
-
- {/* TODO: I am not sure if I realized that before but all instances of this `CompanySearch` are actually filtering by the user's country currently. I am not sure whether we want that. If we decide to keep it, we should also filter the Hugo-generated list pages. */}
- {
- location.href = '/company/' + suggestion.document.slug;
- }}
- placeholder={t('select-company', 'cdb')}
- debug={true}
- style="margin-top: 15px;"
- filters={this.props.filters}
- anchorize={true}
- />
-
- );
- }
-
- static propTypes = {
- filters: PropTypes.arrayOf(PropTypes.string),
- };
-}
-
-let company_list_div = document.getElementById('company-list');
-if (company_list_div) {
- render(, company_list_div.parentElement, company_list_div);
-}
-let search_div = document.getElementById('company-search');
-if (search_div) {
- let search_filters = search_div.dataset.filterCategory;
- render(
- ,
- search_div.parentElement,
- search_div
- );
-}
diff --git a/src/company-list.tsx b/src/company-list.tsx
new file mode 100644
index 000000000..36d483b9c
--- /dev/null
+++ b/src/company-list.tsx
@@ -0,0 +1,109 @@
+import { render } from 'preact';
+import { SearchBar } from './Components/SearchBar';
+import { IntlProvider, Text } from 'preact-i18n';
+import t from './Utility/i18n';
+import { Privacy, PRIVACY_ACTIONS } from './Utility/Privacy';
+import Scrollspy from 'react-scrollspy';
+import { useEffect } from 'preact/hooks';
+
+if (!Privacy.isAllowed(PRIVACY_ACTIONS.SEARCH) && document.getElementById('aa-search-input')) {
+ const searchInput = document.getElementById('aa-search-input');
+ if (searchInput) searchInput.style.display = 'none';
+}
+
+const CompanyList = () => {
+ useEffect(() => {
+ const handleScroll = () => {
+ const controls = document.getElementById('company-list-controls');
+ if (controls) {
+ if (window.scrollY > controls.offsetTop) {
+ controls.classList.add('sticky');
+ document.body.classList.add('sticky-offset');
+ }
+ if (window.scrollY < controls.offsetTop + 200) {
+ controls.classList.remove('sticky');
+ document.body.classList.remove('sticky-offset');
+ }
+ }
+ };
+
+ window.addEventListener('scroll', handleScroll);
+
+ return () => {
+ window.removeEventListener('scroll', handleScroll);
+ };
+ }, []);
+
+ const alphabet = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
+ const anchorIds = alphabet.map((x) => (x === '#' ? 'numbers' : x) + '-container');
+ const anchorLinks = alphabet.map((x) => (
+
+
+ {x}
+
+
+ ));
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {anchorLinks}
+
+
+
+
+
+ );
+};
+
+type CompanySearchProps = { filters: string[] | undefined };
+
+const CompanySearch = (props: CompanySearchProps) => (
+
+ {/* TODO: I am not sure if I realized that before but all instances of this `CompanySearch` are actually filtering by the user's country currently. I am not sure whether we want that. If we decide to keep it, we should also filter the Hugo-generated list pages. */}
+
+
+);
+
+const companyListDiv = document.getElementById('company-list');
+if (companyListDiv) {
+ render(, companyListDiv.parentElement!, companyListDiv);
+}
+const searchDiv = document.getElementById('company-search');
+if (searchDiv) {
+ const searchFilters = searchDiv.dataset.filterCategory;
+ render(
+ ,
+ searchDiv.parentElement!,
+ searchDiv
+ );
+}
diff --git a/webpack.common.js b/webpack.common.js
index d2c990959..2864f95d4 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -8,7 +8,7 @@ module.exports = {
home: './src/home.tsx',
generator: './src/generator.tsx',
app: './src/app.tsx',
- 'company-list': './src/company-list.js',
+ 'company-list': './src/company-list.tsx',
'my-requests': './src/my-requests.tsx',
'privacy-controls': './src/privacy-controls.tsx',
'suggest-edit': './src/suggest-edit.js',
diff --git a/yarn.lock b/yarn.lock
index 612d21d4e..c874afb4b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1832,6 +1832,13 @@
"@types/react" "*"
"@types/react-instantsearch-core" "*"
+"@types/react-scrollspy@^3.3.9":
+ version "3.3.9"
+ resolved "https://registry.yarnpkg.com/@types/react-scrollspy/-/react-scrollspy-3.3.9.tgz#d075867cb76527bfc3429bf0f343fb4bc533d9c7"
+ integrity sha512-rOi649b3M5j/AHU1vEGsXbA3LgkeiOnhrOJnPrBOjdR82d9Dao+iI0LWqqR9xjiPtFMnWWjog+w2yjuAtTvFdw==
+ dependencies:
+ "@types/react" "*"
+
"@types/react@*":
version "18.0.9"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878"