@transferwise/components 0.0.0-experimental-9a61b0a → 0.0.0-experimental-73858ea
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.esm.js +183 -257
- package/build/index.esm.js.map +1 -1
- package/build/index.js +182 -256
- package/build/index.js.map +1 -1
- package/build/types/common/focusBoundary/FocusBoundary.d.ts +2 -2
- package/build/types/common/focusBoundary/FocusBoundary.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/common/bottomSheet/__snapshots__/BottomSheet.spec.tsx.snap +8 -1
- package/src/common/focusBoundary/FocusBoundary.tsx +9 -32
- package/build/types/common/focusBoundary/utils/getFocusableElements.d.ts +0 -2
- package/build/types/common/focusBoundary/utils/getFocusableElements.d.ts.map +0 -1
- package/build/types/common/focusBoundary/utils/index.d.ts +0 -3
- package/build/types/common/focusBoundary/utils/index.d.ts.map +0 -1
- package/build/types/common/focusBoundary/utils/resetFocus.d.ts +0 -2
- package/build/types/common/focusBoundary/utils/resetFocus.d.ts.map +0 -1
- package/src/common/focusBoundary/FocusBoundary.spec.tsx +0 -66
- package/src/common/focusBoundary/__snapshots__/FocusBoundary.spec.tsx.snap +0 -16
- package/src/common/focusBoundary/utils/getFocusableElements.js +0 -25
- package/src/common/focusBoundary/utils/getFocusableElements.spec.js +0 -51
- package/src/common/focusBoundary/utils/index.js +0 -2
- package/src/common/focusBoundary/utils/resetFocus.js +0 -23
- package/src/common/focusBoundary/utils/resetFocus.spec.js +0 -103
package/build/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import React__default, { forwardRef, cloneElement, useState, useRef, useMemo, useEffect, useLayoutEffect, createContext, useContext,
|
|
4
|
+
import React__default, { forwardRef, cloneElement, useState, useRef, useMemo, useEffect, useCallback, useLayoutEffect, createContext, useContext, PureComponent, createRef, Component, Children, Fragment as Fragment$1 } from 'react';
|
|
5
5
|
import { ChevronUp, CrossCircleFill, Cross, NavigateAway, Check, Info as Info$1, Alert as Alert$2, ClockBorderless, CheckCircle, InfoCircle, Warning, CrossCircle, Clock, Briefcase, Person, ArrowLeft, QuestionMarkCircle, AlertCircle, Search, ChevronDown, CheckCircleFill, ArrowRight, Download, ClockFill, Upload as Upload$2, Document, Plus, PlusCircle, AlertCircleFill } from '@transferwise/icons';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
7
|
import { defineMessages, useIntl, injectIntl, IntlProvider } from 'react-intl';
|
|
@@ -10,8 +10,9 @@ import { formatDate, formatNumber, formatMoney, formatAmount } from '@transferwi
|
|
|
10
10
|
import throttle from 'lodash.throttle';
|
|
11
11
|
import { CSSTransition } from 'react-transition-group';
|
|
12
12
|
import { createPortal } from 'react-dom';
|
|
13
|
-
import {
|
|
13
|
+
import { FocusScope } from '@react-aria/focus';
|
|
14
14
|
import mergeRefs from 'react-merge-refs';
|
|
15
|
+
import { isUndefined, isKey, isNumber, isEmpty, isNull, isArray } from '@transferwise/neptune-validation';
|
|
15
16
|
import { usePopper } from 'react-popper';
|
|
16
17
|
import { Transition, Listbox } from '@headlessui/react';
|
|
17
18
|
import { useId } from '@radix-ui/react-id';
|
|
@@ -1457,268 +1458,23 @@ function getInitials(name) {
|
|
|
1457
1458
|
return allInitials[0] + allInitials.slice(-1);
|
|
1458
1459
|
}
|
|
1459
1460
|
|
|
1460
|
-
const THROTTLE_MS = 100;
|
|
1461
|
-
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
1462
|
-
const useClientWidth = ({
|
|
1463
|
-
ref,
|
|
1464
|
-
throttleMs = THROTTLE_MS
|
|
1465
|
-
}) => {
|
|
1466
|
-
const [clientWidth, setClientWidth] = useState(null);
|
|
1467
|
-
useIsomorphicLayoutEffect(() => {
|
|
1468
|
-
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
1469
|
-
const updateClientWidth = () => {
|
|
1470
|
-
if (ref) {
|
|
1471
|
-
// when `ref` is a window
|
|
1472
|
-
if ('innerWidth' in ref) {
|
|
1473
|
-
setClientWidth(ref.innerWidth);
|
|
1474
|
-
}
|
|
1475
|
-
// when `ref` is an element
|
|
1476
|
-
else if (ref.current) {
|
|
1477
|
-
setClientWidth(ref.current.clientWidth);
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
|
-
// This assignment saves a reference to the function so it will be the same passed to both addEventListener removeEventListener.
|
|
1482
|
-
// If throttle gets passed directly to both add and removeEventListenet the results will be that the event
|
|
1483
|
-
// won't get removed even if the component is unmounted.
|
|
1484
|
-
const attachedFunction = throttle(updateClientWidth, throttleMs);
|
|
1485
|
-
window.addEventListener('resize', attachedFunction, true);
|
|
1486
|
-
// using requestAnimationFrame to perform the calculation before the next repaint
|
|
1487
|
-
// getting width earlier causes issues in animations when used with react-transition-group
|
|
1488
|
-
window.requestAnimationFrame(updateClientWidth);
|
|
1489
|
-
return () => window.removeEventListener('resize', attachedFunction, true);
|
|
1490
|
-
}, []);
|
|
1491
|
-
return [clientWidth];
|
|
1492
|
-
};
|
|
1493
|
-
useClientWidth.THROTTLE_MS = THROTTLE_MS;
|
|
1494
|
-
|
|
1495
|
-
const useConditionalListener = ({
|
|
1496
|
-
attachListener,
|
|
1497
|
-
callback,
|
|
1498
|
-
eventType,
|
|
1499
|
-
parent
|
|
1500
|
-
}) => {
|
|
1501
|
-
useEffect(() => {
|
|
1502
|
-
if (attachListener && !isUndefined(parent)) {
|
|
1503
|
-
parent.addEventListener(eventType, callback, true);
|
|
1504
|
-
}
|
|
1505
|
-
return () => {
|
|
1506
|
-
if (!isUndefined(parent)) {
|
|
1507
|
-
parent.removeEventListener(eventType, callback, true);
|
|
1508
|
-
}
|
|
1509
|
-
};
|
|
1510
|
-
}, [attachListener, callback, eventType, parent]);
|
|
1511
|
-
};
|
|
1512
|
-
|
|
1513
|
-
const DirectionContext = /*#__PURE__*/createContext(Direction.LTR);
|
|
1514
|
-
const DirectionProvider = ({
|
|
1515
|
-
direction,
|
|
1516
|
-
children
|
|
1517
|
-
}) => {
|
|
1518
|
-
return /*#__PURE__*/jsx(DirectionContext.Provider, {
|
|
1519
|
-
value: direction,
|
|
1520
|
-
children: children
|
|
1521
|
-
});
|
|
1522
|
-
};
|
|
1523
|
-
|
|
1524
|
-
const useDirection = () => {
|
|
1525
|
-
const direction = useContext(DirectionContext);
|
|
1526
|
-
return {
|
|
1527
|
-
direction,
|
|
1528
|
-
isRTL: direction === 'rtl'
|
|
1529
|
-
};
|
|
1530
|
-
};
|
|
1531
|
-
|
|
1532
|
-
const ObserverParams = {
|
|
1533
|
-
threshold: 0.1
|
|
1534
|
-
};
|
|
1535
|
-
|
|
1536
|
-
/**
|
|
1537
|
-
* useHasIntersected.
|
|
1538
|
-
* Use this custom hook to detect when an element has became visible inside the viewport. This hook checks only if the intersection happend.
|
|
1539
|
-
* Once the intersection has happened the hook will not return false even if the element gets out of the viewport.
|
|
1540
|
-
*
|
|
1541
|
-
* @param elRef.elRef
|
|
1542
|
-
* @param {object} [elRef] - node object that contains a react reference to the element that needs to be observed.
|
|
1543
|
-
* @param {strimng} [loading = 'eager'] - string that contains the type of loading.
|
|
1544
|
-
* @param elRef.loading
|
|
1545
|
-
* @usage `const [hasIntersected] = useHasIntersected({imageRef,loading});`
|
|
1546
|
-
*/
|
|
1547
|
-
const useHasIntersected = ({
|
|
1548
|
-
elRef,
|
|
1549
|
-
loading
|
|
1550
|
-
}) => {
|
|
1551
|
-
const [hasIntersected, setHasIntersected] = useState(false);
|
|
1552
|
-
const {
|
|
1553
|
-
current
|
|
1554
|
-
} = elRef || {};
|
|
1555
|
-
const isValidReference = () => {
|
|
1556
|
-
return elRef && current;
|
|
1557
|
-
};
|
|
1558
|
-
const handleOnIntersect = (entries, observer) => {
|
|
1559
|
-
entries.forEach(entry => {
|
|
1560
|
-
if (entry.isIntersecting) {
|
|
1561
|
-
setHasIntersected(true);
|
|
1562
|
-
observer.unobserve(current);
|
|
1563
|
-
}
|
|
1564
|
-
});
|
|
1565
|
-
};
|
|
1566
|
-
useEffect(() => {
|
|
1567
|
-
let observer;
|
|
1568
|
-
let didCancel = false;
|
|
1569
|
-
|
|
1570
|
-
// Check if window is define for SSR and Old browsers fallback
|
|
1571
|
-
if (typeof window === 'undefined' || !window.IntersectionObserver || !isValidReference()) {
|
|
1572
|
-
setHasIntersected(true);
|
|
1573
|
-
} else if (!didCancel) {
|
|
1574
|
-
observer = new IntersectionObserver(handleOnIntersect, ObserverParams);
|
|
1575
|
-
observer.observe(current);
|
|
1576
|
-
}
|
|
1577
|
-
return () => {
|
|
1578
|
-
didCancel = true;
|
|
1579
|
-
if (observer) {
|
|
1580
|
-
observer.unobserve(current);
|
|
1581
|
-
}
|
|
1582
|
-
};
|
|
1583
|
-
}, [elRef]);
|
|
1584
|
-
if (loading === 'eager') {
|
|
1585
|
-
return [false];
|
|
1586
|
-
}
|
|
1587
|
-
return [hasIntersected];
|
|
1588
|
-
};
|
|
1589
|
-
|
|
1590
|
-
const useLayout = () => {
|
|
1591
|
-
const windowReference = typeof window === 'undefined' ? undefined : window;
|
|
1592
|
-
const [breakpoint, setBreakpoint] = useState();
|
|
1593
|
-
const [clientWidth] = useClientWidth({
|
|
1594
|
-
ref: windowReference
|
|
1595
|
-
});
|
|
1596
|
-
useEffect(() => {
|
|
1597
|
-
if (!clientWidth) {
|
|
1598
|
-
return;
|
|
1599
|
-
}
|
|
1600
|
-
if (clientWidth <= Breakpoint.EXTRA_SMALL) {
|
|
1601
|
-
setBreakpoint(Breakpoint.EXTRA_SMALL);
|
|
1602
|
-
return;
|
|
1603
|
-
}
|
|
1604
|
-
if (Breakpoint.EXTRA_SMALL < clientWidth && clientWidth <= Breakpoint.SMALL) {
|
|
1605
|
-
setBreakpoint(Breakpoint.SMALL);
|
|
1606
|
-
return;
|
|
1607
|
-
}
|
|
1608
|
-
if (Breakpoint.SMALL < clientWidth && clientWidth <= Breakpoint.MEDIUM) {
|
|
1609
|
-
setBreakpoint(Breakpoint.MEDIUM);
|
|
1610
|
-
return;
|
|
1611
|
-
}
|
|
1612
|
-
if (Breakpoint.MEDIUM < clientWidth && clientWidth <= Breakpoint.LARGE) {
|
|
1613
|
-
setBreakpoint(Breakpoint.LARGE);
|
|
1614
|
-
return;
|
|
1615
|
-
}
|
|
1616
|
-
if (Breakpoint.LARGE < clientWidth) {
|
|
1617
|
-
setBreakpoint(Breakpoint.EXTRA_LARGE);
|
|
1618
|
-
}
|
|
1619
|
-
}, [clientWidth]);
|
|
1620
|
-
return {
|
|
1621
|
-
isMobile: !!breakpoint && [Breakpoint.EXTRA_SMALL, Breakpoint.SMALL].includes(breakpoint),
|
|
1622
|
-
isExtraSmall: breakpoint === Breakpoint.EXTRA_SMALL,
|
|
1623
|
-
isSmall: breakpoint === Breakpoint.SMALL,
|
|
1624
|
-
isMedium: breakpoint === Breakpoint.MEDIUM,
|
|
1625
|
-
isLarge: breakpoint === Breakpoint.LARGE,
|
|
1626
|
-
isExtraLarge: breakpoint === Breakpoint.EXTRA_LARGE
|
|
1627
|
-
};
|
|
1628
|
-
};
|
|
1629
|
-
|
|
1630
|
-
/**
|
|
1631
|
-
* This function returns the first and the last focusable elements within a node.
|
|
1632
|
-
*
|
|
1633
|
-
* @param {Node} focusBoundaryContainer - the area that contains focused elements.
|
|
1634
|
-
* @returns {object} focusableEls - which contains the first focusable element and the last focusable element. First and last can be the same element if area contains one or none focusable element.
|
|
1635
|
-
*/
|
|
1636
|
-
|
|
1637
|
-
const getFocusableElements = focusBoundaryContainer => {
|
|
1638
|
-
const focusableEls = {
|
|
1639
|
-
first: focusBoundaryContainer,
|
|
1640
|
-
last: focusBoundaryContainer
|
|
1641
|
-
};
|
|
1642
|
-
if (focusBoundaryContainer?.querySelectorAll) {
|
|
1643
|
-
const allEls = [...focusBoundaryContainer.querySelectorAll('a, button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])')].filter(element => !element.hasAttribute('disabled'));
|
|
1644
|
-
if (allEls.length > 0) {
|
|
1645
|
-
[focusableEls.first] = allEls;
|
|
1646
|
-
focusableEls.last = allEls[allEls.length - 1];
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
return focusableEls;
|
|
1650
|
-
};
|
|
1651
|
-
|
|
1652
|
-
/**
|
|
1653
|
-
* This function resets the focus to either last of first focusable elements within a node.
|
|
1654
|
-
*
|
|
1655
|
-
* @param {object} focusableEls - contains the first last of first focusable elements within a node.
|
|
1656
|
-
* @param {object} event - the triggered event
|
|
1657
|
-
*/
|
|
1658
|
-
|
|
1659
|
-
const resetFocus = ({
|
|
1660
|
-
focusableEls: {
|
|
1661
|
-
first,
|
|
1662
|
-
last
|
|
1663
|
-
},
|
|
1664
|
-
event
|
|
1665
|
-
}) => {
|
|
1666
|
-
const {
|
|
1667
|
-
activeElement
|
|
1668
|
-
} = document;
|
|
1669
|
-
if (event.shiftKey && activeElement === first) {
|
|
1670
|
-
if (last) {
|
|
1671
|
-
last.focus();
|
|
1672
|
-
}
|
|
1673
|
-
event.preventDefault();
|
|
1674
|
-
}
|
|
1675
|
-
if (!event.shiftKey && activeElement === last) {
|
|
1676
|
-
if (first) {
|
|
1677
|
-
first.focus();
|
|
1678
|
-
}
|
|
1679
|
-
event.preventDefault();
|
|
1680
|
-
}
|
|
1681
|
-
};
|
|
1682
|
-
|
|
1683
|
-
const {
|
|
1684
|
-
TAB
|
|
1685
|
-
} = Key;
|
|
1686
1461
|
const FocusBoundary = ({
|
|
1687
1462
|
children
|
|
1688
1463
|
}) => {
|
|
1689
|
-
const
|
|
1690
|
-
const parent = isUndefined(document) ? undefined : document;
|
|
1691
|
-
const [focusableEls, setFocusableEls] = useState({});
|
|
1464
|
+
const wrapperReference = useRef(null);
|
|
1692
1465
|
useEffect(() => {
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
});
|
|
1697
|
-
setFocusableEls(getFocusableElements(boundaryReference.current));
|
|
1698
|
-
}
|
|
1466
|
+
wrapperReference.current?.focus({
|
|
1467
|
+
preventScroll: true
|
|
1468
|
+
});
|
|
1699
1469
|
}, []);
|
|
1700
|
-
// If event type is Tab the resetFocus will force the focus to either the first focusable or last in boundaryRef .
|
|
1701
|
-
useConditionalListener({
|
|
1702
|
-
eventType: 'keydown',
|
|
1703
|
-
callback: event => {
|
|
1704
|
-
if (isKey({
|
|
1705
|
-
keyType: TAB,
|
|
1706
|
-
event
|
|
1707
|
-
})) {
|
|
1708
|
-
resetFocus({
|
|
1709
|
-
event,
|
|
1710
|
-
focusableEls
|
|
1711
|
-
});
|
|
1712
|
-
}
|
|
1713
|
-
},
|
|
1714
|
-
attachListener: true,
|
|
1715
|
-
parent
|
|
1716
|
-
});
|
|
1717
1470
|
return /*#__PURE__*/jsx("div", {
|
|
1718
|
-
ref:
|
|
1471
|
+
ref: wrapperReference,
|
|
1719
1472
|
tabIndex: -1,
|
|
1720
|
-
|
|
1721
|
-
|
|
1473
|
+
children: /*#__PURE__*/jsx(FocusScope, {
|
|
1474
|
+
contain: true,
|
|
1475
|
+
restoreFocus: true,
|
|
1476
|
+
children: children
|
|
1477
|
+
})
|
|
1722
1478
|
});
|
|
1723
1479
|
};
|
|
1724
1480
|
var FocusBoundary$1 = FocusBoundary;
|
|
@@ -1974,6 +1730,176 @@ SlidingPanel.defaultProps = {
|
|
|
1974
1730
|
};
|
|
1975
1731
|
var SlidingPanel$1 = SlidingPanel;
|
|
1976
1732
|
|
|
1733
|
+
const THROTTLE_MS = 100;
|
|
1734
|
+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
1735
|
+
const useClientWidth = ({
|
|
1736
|
+
ref,
|
|
1737
|
+
throttleMs = THROTTLE_MS
|
|
1738
|
+
}) => {
|
|
1739
|
+
const [clientWidth, setClientWidth] = useState(null);
|
|
1740
|
+
useIsomorphicLayoutEffect(() => {
|
|
1741
|
+
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
1742
|
+
const updateClientWidth = () => {
|
|
1743
|
+
if (ref) {
|
|
1744
|
+
// when `ref` is a window
|
|
1745
|
+
if ('innerWidth' in ref) {
|
|
1746
|
+
setClientWidth(ref.innerWidth);
|
|
1747
|
+
}
|
|
1748
|
+
// when `ref` is an element
|
|
1749
|
+
else if (ref.current) {
|
|
1750
|
+
setClientWidth(ref.current.clientWidth);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
// This assignment saves a reference to the function so it will be the same passed to both addEventListener removeEventListener.
|
|
1755
|
+
// If throttle gets passed directly to both add and removeEventListenet the results will be that the event
|
|
1756
|
+
// won't get removed even if the component is unmounted.
|
|
1757
|
+
const attachedFunction = throttle(updateClientWidth, throttleMs);
|
|
1758
|
+
window.addEventListener('resize', attachedFunction, true);
|
|
1759
|
+
// using requestAnimationFrame to perform the calculation before the next repaint
|
|
1760
|
+
// getting width earlier causes issues in animations when used with react-transition-group
|
|
1761
|
+
window.requestAnimationFrame(updateClientWidth);
|
|
1762
|
+
return () => window.removeEventListener('resize', attachedFunction, true);
|
|
1763
|
+
}, []);
|
|
1764
|
+
return [clientWidth];
|
|
1765
|
+
};
|
|
1766
|
+
useClientWidth.THROTTLE_MS = THROTTLE_MS;
|
|
1767
|
+
|
|
1768
|
+
const useConditionalListener = ({
|
|
1769
|
+
attachListener,
|
|
1770
|
+
callback,
|
|
1771
|
+
eventType,
|
|
1772
|
+
parent
|
|
1773
|
+
}) => {
|
|
1774
|
+
useEffect(() => {
|
|
1775
|
+
if (attachListener && !isUndefined(parent)) {
|
|
1776
|
+
parent.addEventListener(eventType, callback, true);
|
|
1777
|
+
}
|
|
1778
|
+
return () => {
|
|
1779
|
+
if (!isUndefined(parent)) {
|
|
1780
|
+
parent.removeEventListener(eventType, callback, true);
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
}, [attachListener, callback, eventType, parent]);
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1786
|
+
const DirectionContext = /*#__PURE__*/createContext(Direction.LTR);
|
|
1787
|
+
const DirectionProvider = ({
|
|
1788
|
+
direction,
|
|
1789
|
+
children
|
|
1790
|
+
}) => {
|
|
1791
|
+
return /*#__PURE__*/jsx(DirectionContext.Provider, {
|
|
1792
|
+
value: direction,
|
|
1793
|
+
children: children
|
|
1794
|
+
});
|
|
1795
|
+
};
|
|
1796
|
+
|
|
1797
|
+
const useDirection = () => {
|
|
1798
|
+
const direction = useContext(DirectionContext);
|
|
1799
|
+
return {
|
|
1800
|
+
direction,
|
|
1801
|
+
isRTL: direction === 'rtl'
|
|
1802
|
+
};
|
|
1803
|
+
};
|
|
1804
|
+
|
|
1805
|
+
const ObserverParams = {
|
|
1806
|
+
threshold: 0.1
|
|
1807
|
+
};
|
|
1808
|
+
|
|
1809
|
+
/**
|
|
1810
|
+
* useHasIntersected.
|
|
1811
|
+
* Use this custom hook to detect when an element has became visible inside the viewport. This hook checks only if the intersection happend.
|
|
1812
|
+
* Once the intersection has happened the hook will not return false even if the element gets out of the viewport.
|
|
1813
|
+
*
|
|
1814
|
+
* @param elRef.elRef
|
|
1815
|
+
* @param {object} [elRef] - node object that contains a react reference to the element that needs to be observed.
|
|
1816
|
+
* @param {strimng} [loading = 'eager'] - string that contains the type of loading.
|
|
1817
|
+
* @param elRef.loading
|
|
1818
|
+
* @usage `const [hasIntersected] = useHasIntersected({imageRef,loading});`
|
|
1819
|
+
*/
|
|
1820
|
+
const useHasIntersected = ({
|
|
1821
|
+
elRef,
|
|
1822
|
+
loading
|
|
1823
|
+
}) => {
|
|
1824
|
+
const [hasIntersected, setHasIntersected] = useState(false);
|
|
1825
|
+
const {
|
|
1826
|
+
current
|
|
1827
|
+
} = elRef || {};
|
|
1828
|
+
const isValidReference = () => {
|
|
1829
|
+
return elRef && current;
|
|
1830
|
+
};
|
|
1831
|
+
const handleOnIntersect = (entries, observer) => {
|
|
1832
|
+
entries.forEach(entry => {
|
|
1833
|
+
if (entry.isIntersecting) {
|
|
1834
|
+
setHasIntersected(true);
|
|
1835
|
+
observer.unobserve(current);
|
|
1836
|
+
}
|
|
1837
|
+
});
|
|
1838
|
+
};
|
|
1839
|
+
useEffect(() => {
|
|
1840
|
+
let observer;
|
|
1841
|
+
let didCancel = false;
|
|
1842
|
+
|
|
1843
|
+
// Check if window is define for SSR and Old browsers fallback
|
|
1844
|
+
if (typeof window === 'undefined' || !window.IntersectionObserver || !isValidReference()) {
|
|
1845
|
+
setHasIntersected(true);
|
|
1846
|
+
} else if (!didCancel) {
|
|
1847
|
+
observer = new IntersectionObserver(handleOnIntersect, ObserverParams);
|
|
1848
|
+
observer.observe(current);
|
|
1849
|
+
}
|
|
1850
|
+
return () => {
|
|
1851
|
+
didCancel = true;
|
|
1852
|
+
if (observer) {
|
|
1853
|
+
observer.unobserve(current);
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
}, [elRef]);
|
|
1857
|
+
if (loading === 'eager') {
|
|
1858
|
+
return [false];
|
|
1859
|
+
}
|
|
1860
|
+
return [hasIntersected];
|
|
1861
|
+
};
|
|
1862
|
+
|
|
1863
|
+
const useLayout = () => {
|
|
1864
|
+
const windowReference = typeof window === 'undefined' ? undefined : window;
|
|
1865
|
+
const [breakpoint, setBreakpoint] = useState();
|
|
1866
|
+
const [clientWidth] = useClientWidth({
|
|
1867
|
+
ref: windowReference
|
|
1868
|
+
});
|
|
1869
|
+
useEffect(() => {
|
|
1870
|
+
if (!clientWidth) {
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
if (clientWidth <= Breakpoint.EXTRA_SMALL) {
|
|
1874
|
+
setBreakpoint(Breakpoint.EXTRA_SMALL);
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
if (Breakpoint.EXTRA_SMALL < clientWidth && clientWidth <= Breakpoint.SMALL) {
|
|
1878
|
+
setBreakpoint(Breakpoint.SMALL);
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
if (Breakpoint.SMALL < clientWidth && clientWidth <= Breakpoint.MEDIUM) {
|
|
1882
|
+
setBreakpoint(Breakpoint.MEDIUM);
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
if (Breakpoint.MEDIUM < clientWidth && clientWidth <= Breakpoint.LARGE) {
|
|
1886
|
+
setBreakpoint(Breakpoint.LARGE);
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
if (Breakpoint.LARGE < clientWidth) {
|
|
1890
|
+
setBreakpoint(Breakpoint.EXTRA_LARGE);
|
|
1891
|
+
}
|
|
1892
|
+
}, [clientWidth]);
|
|
1893
|
+
return {
|
|
1894
|
+
isMobile: !!breakpoint && [Breakpoint.EXTRA_SMALL, Breakpoint.SMALL].includes(breakpoint),
|
|
1895
|
+
isExtraSmall: breakpoint === Breakpoint.EXTRA_SMALL,
|
|
1896
|
+
isSmall: breakpoint === Breakpoint.SMALL,
|
|
1897
|
+
isMedium: breakpoint === Breakpoint.MEDIUM,
|
|
1898
|
+
isLarge: breakpoint === Breakpoint.LARGE,
|
|
1899
|
+
isExtraLarge: breakpoint === Breakpoint.EXTRA_LARGE
|
|
1900
|
+
};
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1977
1903
|
const INITIAL_Y_POSITION = 0;
|
|
1978
1904
|
const CONTENT_SCROLL_THRESHOLD = 1;
|
|
1979
1905
|
const MOVE_OFFSET_THRESHOLD = 50;
|