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"