{"version":3,"sources":["webpack://switch-website/./src/helpers/hooks/usePrefersReducedMotion.js","webpack://switch-website/./src/components/common/Pagination.js","webpack://switch-website/./src/helpers/hooks/useFilters.js","webpack://switch-website/./src/helpers/hooks/useFiltersFromQueryParameters.js","webpack://switch-website/./src/helpers/hooks/useQueryParameters.js","webpack://switch-website/./src/helpers/hooks/useInterval.js","webpack://switch-website/./src/helpers/hooks/usePagination.js"],"names":["isRenderingOnServer","window","QUERY","getInitialState","matchMedia","matches","StyledPagination","ContentContainer","StyledPageItem","Link","colors","P","PageItem","number","currentPage","setCurrentPage","active","onClick","className","PaginationEllipsis","style","cursor","StyledArrow","Arrow","defaultProps","ArrowNext","disabled","ArrowBefore","StyledSliderPagination","PaginationDot","Pagination","numberOfElements","elementsPerPage","scrollToRef","pages","totalNumberOfPages","Math","ceil","prefersReducedMotion","useState","setPrefersReducedMotion","useEffect","mediaQueryList","listener","event","addEventListener","removeEventListener","usePrefersReducedMotion","changePage","targetPage","smoothScroll","current","goToPage","nextPage","generatePage","key","push","firstPageToShow","max","lastPageToShow","min","i","SliderPagination","currentIndex","setIndex","rotationTimeout","_rotationTimeout","setRotationTimeout","useInterval","dots","newIndex","setTimeout","getItemValue","filterKey","item","path","itemValue","split","forEach","fragment","equalityOperations","array","contains","filter","map","value","name","includes","objectValue","some","obj","string","equals","icontains","toLowerCase","getOperation","schema","type","operator","dataset","filters","i18next","useContext","I18nextContext","lunrIndex","setLunrIndex","__LUNR__","language","useMemo","filteredDataset","textSearchResults","search","searchResults","index","splitString","ref","length","filtersEntries","Object","entries","id","element","filterSchema","equalityOperation","applyFilter","location","allowedFilters","URLSearchParams","queryParameters","setQueryParameters","queryParamsString","toString","newUrl","pathname","history","replaceState","parsedQueryParams","queryParamsForUrl","useQueryParameters","queryParams","setQueryParams","stateSelectedFilters","state","selectedFilters","cleanedQueryParams","objectFilter","handler","timeout","savedCallback","useRef","setInterval","clearInterval","initialElement","finalElement","maxIteration","slice"],"mappings":"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","file":"c44879026cff90d6a0fa6f77969e8562ce675e24-dced62e37929d50f5ba6.js","sourcesContent":["import {useEffect, useState} from 'react';\n\n// Determine if user has \"prefers-reduced-motion\" setting ON.\n// Taken from: https://www.joshwcomeau.com/react/prefers-reduced-motion/#the-hook\n\nconst isRenderingOnServer = typeof window === 'undefined';\n\nconst QUERY = '(prefers-reduced-motion: no-preference)';\nconst getInitialState = () => (isRenderingOnServer ? true : !window.matchMedia(QUERY).matches);\n\nexport default () => {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(getInitialState);\n useEffect(() => {\n const mediaQueryList = window.matchMedia(QUERY);\n const listener = event => setPrefersReducedMotion(!event.matches);\n mediaQueryList.addEventListener('change', listener);\n return () => {\n mediaQueryList.removeEventListener('change', listener);\n };\n }, []);\n return prefersReducedMotion;\n};\n","/** @jsx jsx */\nimport React, {useState} from 'react';\nimport PropTypes from 'prop-types';\n\nimport styled from '@emotion/styled';\nimport {jsx} from '@emotion/react';\n\nimport usePrefersReducedMotion from '../../helpers/hooks/usePrefersReducedMotion';\nimport {smoothScroll} from '../../helpers/utils';\nimport {colors, ContentContainer} from '../../styles/theme';\nimport {Link, P} from './Typography';\nimport CTAArrowSVG from '../../images/clickables/cta_arrow.svg';\nimport useInterval from '../../helpers/hooks/useInterval';\n\n\n/*\n * Constants\n */\nconst ellipsisMargin = 2;\n\n\n/*\n * Private Elements\n */\nconst StyledPagination = styled.div`\n width: 100%;\n\n ${ContentContainer} {\n max-width: 600px;\n margin: auto;\n padding: 30px !important;\n display: flex;\n flex-direction: row;\n justify-content: center;\n }\n`;\n\nconst StyledPageItem = styled(Link)`\n display: flex;\n justify-content: center;\n margin-left: 5px;\n margin-right: 5px;\n width: 20px;\n text-align: center;\n\n &.active {\n cursor: default;\n border-bottom: 2px solid ${colors.darkPurple};\n }\n\n ${P} {\n margin-bottom: 5px;\n }\n`;\n\nconst PageItem = ({number, currentPage, setCurrentPage}) => {\n const setPage = () => setCurrentPage(number);\n const active = number === currentPage;\n\n return (\n \n

{number}

\n
\n );\n};\n\nPageItem.propTypes = {\n number: PropTypes.number.isRequired,\n currentPage: PropTypes.number.isRequired,\n setCurrentPage: PropTypes.func.isRequired,\n};\n\nconst PaginationEllipsis = () => (\n \n

...

\n
\n);\n\nconst StyledArrow = styled(Link)`\n display: flex;\n align-items: center;\n height: 100%;\n margin-top: 2px;\n margin-left: 10px;\n margin-right: 0;\n\n svg {\n stroke: ${colors.black};\n\n * {\n stroke: ${colors.black};\n }\n }\n\n &.inverted {\n margin-left: 0;\n margin-right: 10px;\n transform: rotate(180deg);\n }\n\n &.disabled {\n cursor: default;\n\n svg {\n stroke: ${colors.grey};\n\n * {\n stroke: ${colors.grey};\n }\n }\n }\n`;\n\nconst Arrow = ({onClick = null, className = ''}) => (\n \n \n \n);\n\nArrow.propTypes = {\n onClick: PropTypes.func,\n className: PropTypes.string,\n};\n\nArrow.defaultProps = {\n onClick: null,\n className: '',\n};\n\nconst ArrowNext = ({disabled, onClick}) => (\n \n);\n\nArrowNext.propTypes = {\n disabled: PropTypes.bool.isRequired,\n onClick: PropTypes.func.isRequired,\n};\n\nconst ArrowBefore = ({disabled, onClick}) => (\n \n);\n\nArrowBefore.propTypes = {\n disabled: PropTypes.bool.isRequired,\n onClick: PropTypes.func.isRequired,\n};\n\n\nconst StyledSliderPagination = styled.div`\n padding: 5px;\n`;\n\nconst PaginationDot = styled.span`\n display: inline-block;\n position: relative;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background-color: ${colors.white};\n margin: 10px;\n cursor: pointer;\n\n &.active {\n background-color: ${colors.darkPurple};\n }\n`;\n\n\n/*\n * Public Elements\n */\n\n/**\n * Pagination element\n *\n * @param {number} numberOfElements - total number of elements to paginate\n * @param {number} elementsPerPage - number of elements per page\n * @param {number} currentPage - the current page\n * @param {function} setCurrentPage - the function to change the page\n * @param {Object} scrollToRef - React Ref of the Component to scroll to on page change\n */\nconst Pagination = ({numberOfElements, elementsPerPage, currentPage, setCurrentPage, scrollToRef = null}) => {\n const pages = [];\n const totalNumberOfPages = Math.ceil(numberOfElements / elementsPerPage);\n\n const prefersReducedMotion = usePrefersReducedMotion();\n\n // Define the change page method\n const changePage = targetPage => {\n if (scrollToRef) {\n smoothScroll(scrollToRef.current, prefersReducedMotion);\n }\n setCurrentPage(targetPage);\n };\n\n // Define the prev/next page method\n const goToPage = nextPage => () => {\n if (scrollToRef) {\n smoothScroll(scrollToRef.current, prefersReducedMotion);\n }\n setCurrentPage(nextPage);\n };\n\n // Define the page item generator\n const generatePage = number => (\n \n );\n\n // Show the first page (it is ALWAYS shown)\n pages.push(generatePage(1));\n\n // Calculate which are the first and last pages to show in the component (between page 1 and the actual last, since\n // those are always present)\n const firstPageToShow = Math.max(currentPage - ellipsisMargin, 2);\n const lastPageToShow = Math.min(currentPage + ellipsisMargin, totalNumberOfPages - 1);\n\n // Show the initial ellipsis if needed\n if (firstPageToShow > 2) {\n pages.push();\n }\n\n // Generate the pages\n for (let i = firstPageToShow; i <= lastPageToShow; i++) {\n pages.push(generatePage(i));\n }\n\n // Show the final ellipsis if needed\n if (lastPageToShow < totalNumberOfPages - 1) {\n pages.push();\n }\n\n // Show the last page (it is ALWAYS shown)\n if (totalNumberOfPages > 1) {\n pages.push(generatePage(totalNumberOfPages));\n }\n\n return (\n \n \n \n\n {pages}\n\n = totalNumberOfPages} onClick={goToPage(currentPage + 1)} />\n \n \n );\n};\n\nPagination.propTypes = {\n numberOfElements: PropTypes.number.isRequired,\n elementsPerPage: PropTypes.number.isRequired,\n currentPage: PropTypes.number.isRequired,\n setCurrentPage: PropTypes.func.isRequired,\n scrollToRef: PropTypes.object,\n};\n\nPagination.defaultProps = {\n scrollToRef: null,\n};\n\n/**\n * Slider Pagination element\n *\n * @param {number} numberOfElements - total number of elements to paginate\n * @param {number} currentIndex - the current element's position\n * @param {function} setIndex - the function to change the current element\n * @param {number | null} rotationTimeout - the interval to rotate slides\n */\nconst SliderPagination = ({numberOfElements, currentIndex, setIndex, rotationTimeout = null}) => {\n const [_rotationTimeout, setRotationTimeout] = useState(rotationTimeout);\n\n const setNewIndex = newIndex => {\n // Stop the counter\n setRotationTimeout(null);\n // Set the new index\n setIndex(newIndex);\n\n // Start the counter again from the beginning (note: it must be set asynchronously so that a full render of the\n // \"null\" timeout is executed, in order to reset the interval)\n setTimeout(() => setRotationTimeout(rotationTimeout), 0);\n };\n\n // Update the current slide being displayed\n useInterval(() => {\n // If we are at the last one, go to the beginning\n const newIndex = currentIndex + 1 >= numberOfElements ? 0 : currentIndex + 1;\n setIndex(newIndex);\n }, _rotationTimeout);\n\n // Add one \"dot\" for each page\n const dots = [];\n for (let i = 0; i < numberOfElements; i++) {\n dots.push(\n setNewIndex(i)} />,\n );\n }\n\n // Return\n return (\n \n {dots}\n \n );\n};\n\nSliderPagination.propTypes = {\n numberOfElements: PropTypes.number.isRequired,\n currentIndex: PropTypes.number.isRequired,\n setIndex: PropTypes.func.isRequired,\n rotationTimeout: PropTypes.number,\n};\n\nSliderPagination.defaultProps = {\n rotationTimeout: null,\n};\n\n\n/*\n * Exports\n */\nexport default Pagination;\nexport {\n SliderPagination,\n};\n","import {useContext, useEffect, useMemo, useState} from 'react';\nimport {I18nextContext} from 'gatsby-plugin-react-i18next';\n\n\n/*\n * Helpers\n */\n/**\n * Get the value of an item we are applying filters to\n *\n * @param {string} filterKey - the name of the filter to apply\n * @param {Object} item - the item to apply the filter to\n * @param {string} path - the object path to the item's value (ex: payment_method.countries)\n * @returns {*} the item's value\n */\nconst getItemValue = (filterKey, item, path) => {\n // If the schema has no path, just return the property with the filter's name\n if (!path) {\n return item[filterKey];\n }\n\n // Otherwise, split the path and iterate it until finding the value\n let itemValue = item;\n path.split('.').forEach(fragment => {\n itemValue = itemValue[fragment];\n });\n\n // Return\n return itemValue;\n};\n\n// Equality checks\n/* eslint-disable max-len */\nconst checkEquality = (filter, item, path) => filter.value === getItemValue(filter.key, item, path);\nconst checkInsensitiveContains = (filter, item, path) => filter.value.toLowerCase().includes(getItemValue(filter.key, item, path).toLowerCase());\nconst checkArrayContains = (filter, item, path) => getItemValue(filter.key, item, path).map(value => value.name).includes(filter.value);\nconst checkArrayObjectValue = (filter, item, path) => getItemValue(filter.key, item, path).some(obj => obj[filter.key] === filter.value);\n/* eslint-enable max-len */\n\n// Available operators and their equality functions\nconst equalityOperations = {\n /* eslint-disable i18next/no-literal-string */\n array: {\n contains: checkArrayContains,\n objectValue: checkArrayObjectValue,\n },\n string: {\n equals: checkEquality,\n icontains: checkInsensitiveContains,\n },\n /* eslint-enable i18next/no-literal-string */\n};\n\n/**\n * Apply a set of filters to an element to check if it matches all of them\n *\n * @param {Object} schema - the filter's schema\n * @returns {function} the equality function to use\n */\nconst getOperation = schema => equalityOperations[schema.type][schema.operator] || null;\n\n/**\n * Apply a set of filters to an element to check if it matches all of them\n *\n * @param {Object} element - the element to apply the filters tp\n * @param {Object} filters - the filters to apply\n * @param {Object} schema - the filters schema\n * @returns {boolean} whether the element passed all the filters or not\n */\nconst applyFilter = (element, filters, schema) => {\n // Apply all the filters to the element\n for (let i = 0; i < filters.length; i++) {\n const filter = filters[i];\n\n // Only filter by this key if there is a valid value\n if (filter.value) {\n const filterSchema = schema[filter.key];\n const equalityOperation = !!filterSchema ? getOperation(filterSchema) : null;\n\n // The filter check fails if:\n // a) there is no equality operator for that filter\n // b) the equality operation returned false\n if (!equalityOperation || !equalityOperation(filter, element, filterSchema.path)) {\n return false;\n }\n }\n }\n\n // Return true if it passed all checks\n return true;\n};\n\n\n/*\n * Hook\n */\n/**\n * Apply filters to a dataset\n *\n * The function is memoized as to avoid unnecessary calculations for the same arguments\n *\n * @param {Object[]} dataset - the dataset to filter\n * @param {Object} filters - the filters to apply\n * @param {Object} schema - the filters schema\n * @returns {Object[]} the filtered elements\n */\nexport default (dataset, filters, schema) => {\n const i18next = useContext(I18nextContext);\n const [lunrIndex, setLunrIndex] = useState(null);\n\n // Make the Lunr index available\n useEffect(() => {\n setLunrIndex(window.__LUNR__[i18next.language]);\n }, [i18next.language]);\n\n // Return filtered data\n return useMemo(() => {\n const filteredDataset = [];\n let textSearchResults = null;\n\n // If there is a search query and the Lunr index is available, ge those results first\n if (filters.search && lunrIndex) {\n const searchResults = lunrIndex.index.search(filters.search);\n textSearchResults = searchResults ? searchResults.map(({ref}) => {\n const splitString = ref.split('_');\n return splitString[splitString.length - 1];\n }) : null;\n }\n\n // Only use the other filters if there was NOT a text search, or it had results\n if (!textSearchResults || textSearchResults.length) {\n const filtersEntries = Object.entries(filters)\n .filter(filter => filter[0] !== 'search')\n .map(filter => ({key: filter[0], value: filter[1]}));\n\n // Iterate each element of the dataset and apply the filters\n dataset.forEach(item => {\n // If there are text search results and they dont match the item in the dataset, there's not a match\n if (textSearchResults && !textSearchResults.includes(item.id)) return;\n\n // Apply the remaining filters to the item\n if (applyFilter(item, filtersEntries, schema)) {\n filteredDataset.push(item);\n }\n });\n }\n\n // Return\n return filteredDataset;\n }, [filters, dataset, lunrIndex, schema]);\n};\n","import useQueryParameters from './useQueryParameters';\nimport {objectFilter} from '../utils';\n\n\n/**\n * Get filters from URL query parameters and location.state\n *\n * @param {Object} location - the location (preferably from a page's props, but can be window.location)\n * @param {string[]} allowedFilters - list of allowed keys for the filters\n * @returns {[{}, function]} - selected filters; method to set URL query params\n */\nexport default (location, allowedFilters) => {\n // Get the current query params, as well as its setter\n const [queryParams, setQueryParams] = useQueryParameters(location);\n\n // Get selected filters from location.state and from URL query params\n const stateSelectedFilters = location.state?.selectedFilters || {};\n const cleanedQueryParams = objectFilter(queryParams, allowedFilters);\n const filters = ({...cleanedQueryParams, ...stateSelectedFilters});\n\n // Return the selected filters and the method to se URL query params\n return [filters, setQueryParams];\n};\n","import {useEffect, useState} from 'react';\n\n\n/**\n * Get and set query string parameters\n *\n * @param {Object} location - the location (preferably from a page's props, but can be window.location)\n * @returns {[{}, function]} the current query params, as well as a method to set them\n */\nexport default location => {\n const [queryParameters, setQueryParameters] = useState(new URLSearchParams(location.search));\n\n useEffect(() => {\n // Set the URL with the parameters\n const queryParamsString = queryParameters.toString();\n const newUrl = queryParamsString.length\n ? `${location.pathname}?${queryParamsString}`\n : `${location.pathname}`;\n window.history.replaceState('{}', '', newUrl);\n });\n\n // Define the setter method\n const setQueryParams = queryParamsForUrl => {\n if (queryParameters.toString() !== new URLSearchParams(queryParamsForUrl).toString()) {\n setQueryParameters(new URLSearchParams(queryParamsForUrl));\n }\n };\n\n // Convert the current parameters into an object (the URLSearchParams object has no such property)\n const parsedQueryParams = {};\n queryParameters.forEach((value, key) => {\n parsedQueryParams[key] = value;\n });\n\n // Return the current parameters, as well as the setter method\n return [parsedQueryParams, setQueryParams];\n};\n","import React, {useEffect, useRef} from 'react';\n\n\n/**\n * A wrapper for Javascript's setInterval (with the same exact signature) that wraps the existing imperative API into\n * React's declarative API.\n * It handles changing either the handler or timeout graciously, as well as pausing the counter by setting the timeout\n * to null.\n *\n * Props to https://overreacted.io/making-setinterval-declarative-with-react-hooks/\n *\n * @param {TimerHandler} handler - function to run in the specified interval\n * @param {number || null} timeout - the interval to use, or null for pausing\n */\nexport default (handler, timeout) => {\n const savedCallback = useRef();\n\n // Remember the latest callback\n useEffect(() => {\n savedCallback.current = handler;\n }, [handler]);\n\n // Set up the interval\n useEffect(() => {\n const tick = () => savedCallback.current();\n if (timeout !== null) {\n const id = setInterval(tick, timeout);\n return () => clearInterval(id);\n }\n\n return undefined;\n }, [timeout]);\n};\n","/**\n * Get a subset of the data list, based on the page number\n *\n * It can handle partial page navigation, such as showing a page of 10 elements, but only adding new 5 elements on the\n * next page (and removing 5 elements from the previous page). This is useful for navigating with arrows instead of page\n * numbers, because you can show several elements in the same page, but only moving one element at a time instead of the\n * whole page.\n *\n * @param {Object[]} dataset - the data to paginate\n * @param {number} currentPage - the current page to show\n * @param {number} elementsPerPage - the number of elements per page to show\n * @returns {Object[]} list of the paginated (subset of) items\n */\nexport default (dataset, currentPage, elementsPerPage) => {\n // Get the first and last elements to display based on the page\n const initialElement = elementsPerPage * (currentPage - 1);\n const finalElement = initialElement + elementsPerPage;\n\n // If the last element is higher than the total number of elements, it means we are in the last page, so we only\n // display until the last item.\n const maxIteration = Math.min(finalElement, dataset.length);\n\n // Return the subset of items related to the specified page\n return dataset.slice(initialElement, maxIteration);\n};\n"],"sourceRoot":""}