@ringcentral/spring-base 1.8.0 → 1.9.1
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/hooks.js +405 -24
- package/index.js +3 -3
- package/package.json +1 -2
- package/src/hooks/index.d.ts +2 -0
- package/src/hooks/useActive/useActive.d.ts +4 -2
- package/src/hooks/useBodyScrollLock/bodyScrollLock.d.ts +31 -0
- package/src/hooks/useBodyScrollLock/internal.d.ts +6 -0
- package/src/hooks/useBodyScrollLock/useBodyScrollLock.d.ts +3 -1
- package/src/hooks/useButton/index.d.ts +1 -0
- package/src/hooks/useButton/useButton.d.ts +2 -0
- package/src/hooks/useVirtuosoExternalWindow/index.d.ts +1 -0
- package/src/hooks/useVirtuosoExternalWindow/useVirtuosoExternalWindow.d.ts +5 -0
- package/src/typings/ComponentTypes.d.ts +5 -4
package/hooks.js
CHANGED
|
@@ -2,17 +2,17 @@ import { unstable_useEventCallback, unstable_ownerDocument, unstable_Timeout, un
|
|
|
2
2
|
export { unstable_useControlled as useControlled } from '@mui/utils';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { useState, useEffect, useRef, useCallback, useLayoutEffect, useMemo } from 'react';
|
|
5
|
-
import uniqueId from 'lodash/uniqueId';
|
|
6
5
|
import { useWindowContext, useResponsiveContext, getMatchDetail } from './contexts.js';
|
|
7
|
-
import
|
|
8
|
-
import '
|
|
6
|
+
import uniqueId from 'lodash/uniqueId';
|
|
7
|
+
import { useButton as useButton$1 } from '@mui/base/useButton';
|
|
8
|
+
import { c as combineProps, a as ariaHideOutside, g as getRefElement, l as logInDev, o as ownerWindow } from './chunks/twMerge.js';
|
|
9
9
|
import './config.js';
|
|
10
10
|
import 'lodash/omit';
|
|
11
11
|
import 'lodash/pick';
|
|
12
|
+
import 'lodash/isPlainObject';
|
|
12
13
|
import debounce from 'lodash/debounce';
|
|
13
14
|
import noop from 'lodash/noop';
|
|
14
15
|
import throttle from 'lodash/throttle';
|
|
15
|
-
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';
|
|
16
16
|
import 'tailwind-merge';
|
|
17
17
|
|
|
18
18
|
const useEventCallback = (func) => {
|
|
@@ -39,18 +39,29 @@ const isNativeButton = (el) => {
|
|
|
39
39
|
['button', 'submit', 'reset'].includes(el?.type)) ||
|
|
40
40
|
(tag === 'A' && el?.href));
|
|
41
41
|
};
|
|
42
|
-
const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
|
|
42
|
+
const useActive = ({ onKeyDown, onKeyUp, onMouseDown, onClick, disabled = false, } = {}) => {
|
|
43
43
|
const [active, setActive] = useState(false);
|
|
44
|
+
const { document: targetDocument = document } = useWindowContext();
|
|
44
45
|
useEffect(() => {
|
|
45
46
|
if (disabled)
|
|
46
47
|
setActive(false);
|
|
47
48
|
}, [disabled]);
|
|
48
49
|
if (disabled) {
|
|
49
50
|
return {
|
|
50
|
-
activeHandlers: { onKeyDown, onKeyUp, onClick },
|
|
51
|
+
activeHandlers: { onKeyDown, onKeyUp, onMouseDown, onClick },
|
|
51
52
|
active: false,
|
|
52
53
|
};
|
|
53
54
|
}
|
|
55
|
+
const handleMouseDown = (event) => {
|
|
56
|
+
onMouseDown?.(event);
|
|
57
|
+
if (event.defaultPrevented) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setActive(true);
|
|
61
|
+
targetDocument.addEventListener('mouseup', () => {
|
|
62
|
+
setActive(false);
|
|
63
|
+
}, { once: true });
|
|
64
|
+
};
|
|
54
65
|
const handleKeyDown = (event) => {
|
|
55
66
|
onKeyDown?.(event);
|
|
56
67
|
if (event.defaultPrevented) {
|
|
@@ -84,7 +95,12 @@ const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
|
|
|
84
95
|
}
|
|
85
96
|
};
|
|
86
97
|
return {
|
|
87
|
-
activeHandlers: {
|
|
98
|
+
activeHandlers: {
|
|
99
|
+
onKeyDown: handleKeyDown,
|
|
100
|
+
onKeyUp: handleKeyUp,
|
|
101
|
+
onMouseDown: handleMouseDown,
|
|
102
|
+
onClick,
|
|
103
|
+
},
|
|
88
104
|
active,
|
|
89
105
|
};
|
|
90
106
|
};
|
|
@@ -155,6 +171,23 @@ const useAnnouncer = (id) => {
|
|
|
155
171
|
}, []);
|
|
156
172
|
};
|
|
157
173
|
|
|
174
|
+
function useButton(parameters) {
|
|
175
|
+
const muiButtonResult = useButton$1(parameters);
|
|
176
|
+
const { active: customActive, activeHandlers } = useActive({
|
|
177
|
+
disabled: parameters.disabled,
|
|
178
|
+
});
|
|
179
|
+
const originalGetRootProps = muiButtonResult.getRootProps;
|
|
180
|
+
const getRootProps = (externalProps) => {
|
|
181
|
+
const mergedProps = combineProps(externalProps, activeHandlers);
|
|
182
|
+
return originalGetRootProps(mergedProps);
|
|
183
|
+
};
|
|
184
|
+
return {
|
|
185
|
+
...muiButtonResult,
|
|
186
|
+
active: customActive,
|
|
187
|
+
getRootProps,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
158
191
|
const useAriaHideOutside = (props = {}) => {
|
|
159
192
|
const { enable = true } = props;
|
|
160
193
|
const { externalWindow, document } = useWindowContext();
|
|
@@ -1445,32 +1478,380 @@ const useFontLoaded = (enabled = true) => {
|
|
|
1445
1478
|
return loaded;
|
|
1446
1479
|
};
|
|
1447
1480
|
|
|
1481
|
+
function createInitialState() {
|
|
1482
|
+
return {
|
|
1483
|
+
entries: [],
|
|
1484
|
+
savedBodyOverflow: '',
|
|
1485
|
+
savedBodyPaddingRight: null,
|
|
1486
|
+
docTouchStartHandler: null,
|
|
1487
|
+
docTouchMoveHandler: null,
|
|
1488
|
+
touchStartY: 0,
|
|
1489
|
+
touchStartX: 0,
|
|
1490
|
+
nonIOSLocked: false,
|
|
1491
|
+
nonIOSReservedScrollBarGap: false,
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
function isIOS(win) {
|
|
1495
|
+
const { platform, maxTouchPoints, userAgent } = win.navigator;
|
|
1496
|
+
return (/iP(ad|hone|od)/.test(platform) ||
|
|
1497
|
+
(platform === 'MacIntel' && maxTouchPoints > 1) ||
|
|
1498
|
+
/iPad|iPhone|iPod/.test(userAgent));
|
|
1499
|
+
}
|
|
1500
|
+
function isScrollableElement(el, win) {
|
|
1501
|
+
const { overflow, overflowX, overflowY } = win.getComputedStyle(el);
|
|
1502
|
+
if (!/(auto|scroll|overlay)/.test(overflow + overflowX + overflowY))
|
|
1503
|
+
return false;
|
|
1504
|
+
return (el.scrollHeight > el.clientHeight + 1 || el.scrollWidth > el.clientWidth + 1);
|
|
1505
|
+
}
|
|
1506
|
+
function isScrollBoundaryHit(scrollable, dx, dy) {
|
|
1507
|
+
if (Math.abs(dy) >= Math.abs(dx)) {
|
|
1508
|
+
if (dy > 0 && scrollable.scrollTop <= 0)
|
|
1509
|
+
return true;
|
|
1510
|
+
return (dy < 0 &&
|
|
1511
|
+
scrollable.scrollTop + scrollable.clientHeight >=
|
|
1512
|
+
scrollable.scrollHeight - 1);
|
|
1513
|
+
}
|
|
1514
|
+
if (dx > 0 && scrollable.scrollLeft <= 0)
|
|
1515
|
+
return true;
|
|
1516
|
+
return (dx < 0 &&
|
|
1517
|
+
scrollable.scrollLeft + scrollable.clientWidth >= scrollable.scrollWidth - 1);
|
|
1518
|
+
}
|
|
1519
|
+
function findNearestScrollableAncestorWithinBoundary(el, win, boundary) {
|
|
1520
|
+
const { body, documentElement } = win.document;
|
|
1521
|
+
let cur = el;
|
|
1522
|
+
while (cur && cur !== body && cur !== documentElement) {
|
|
1523
|
+
if (isScrollableElement(cur, win))
|
|
1524
|
+
return cur;
|
|
1525
|
+
if (cur === boundary)
|
|
1526
|
+
break;
|
|
1527
|
+
cur = cur.parentElement;
|
|
1528
|
+
}
|
|
1529
|
+
return null;
|
|
1530
|
+
}
|
|
1531
|
+
function getActiveEntries(state) {
|
|
1532
|
+
return state.entries.filter((entry) => entry.active);
|
|
1533
|
+
}
|
|
1534
|
+
function getBoundaryForEntry(topEntry) {
|
|
1535
|
+
return topEntry.kind === 'element' ? topEntry.element : null;
|
|
1536
|
+
}
|
|
1537
|
+
function getScrollableWithinBoundary(target, win, boundary) {
|
|
1538
|
+
if (boundary && !boundary.contains(target)) {
|
|
1539
|
+
return null;
|
|
1540
|
+
}
|
|
1541
|
+
return findNearestScrollableAncestorWithinBoundary(target, win, boundary);
|
|
1542
|
+
}
|
|
1543
|
+
function getEntryScopedScrollable(topEntry, target, win, { allowAnonymous = false } = {}) {
|
|
1544
|
+
const boundary = getBoundaryForEntry(topEntry);
|
|
1545
|
+
if (!boundary && !allowAnonymous) {
|
|
1546
|
+
return null;
|
|
1547
|
+
}
|
|
1548
|
+
return getScrollableWithinBoundary(target, win, boundary);
|
|
1549
|
+
}
|
|
1550
|
+
function getAllowTouchMoveScrollable(entry, target, win) {
|
|
1551
|
+
if (entry.allowTouchMove?.(target) !== true)
|
|
1552
|
+
return null;
|
|
1553
|
+
return getEntryScopedScrollable(entry, target, win, {
|
|
1554
|
+
allowAnonymous: true,
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
function canEntryAllowInnerScroll(entry, target, win, dx, dy) {
|
|
1558
|
+
const allowTouchMoveScrollable = getAllowTouchMoveScrollable(entry, target, win);
|
|
1559
|
+
if (allowTouchMoveScrollable) {
|
|
1560
|
+
return !isScrollBoundaryHit(allowTouchMoveScrollable, dx, dy);
|
|
1561
|
+
}
|
|
1562
|
+
const boundaryScrollable = getEntryScopedScrollable(entry, target, win);
|
|
1563
|
+
if (boundaryScrollable) {
|
|
1564
|
+
return !isScrollBoundaryHit(boundaryScrollable, dx, dy);
|
|
1565
|
+
}
|
|
1566
|
+
return false;
|
|
1567
|
+
}
|
|
1568
|
+
function findEntryIndex(state, targetElement, entryKey) {
|
|
1569
|
+
if (entryKey) {
|
|
1570
|
+
return state.entries.findIndex((entry) => entry.key === entryKey);
|
|
1571
|
+
}
|
|
1572
|
+
if (targetElement) {
|
|
1573
|
+
for (let i = state.entries.length - 1; i >= 0; i--) {
|
|
1574
|
+
const entry = state.entries[i];
|
|
1575
|
+
if (entry.kind === 'element' && entry.element === targetElement) {
|
|
1576
|
+
return i;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
return -1;
|
|
1580
|
+
}
|
|
1581
|
+
for (let i = state.entries.length - 1; i >= 0; i--) {
|
|
1582
|
+
if (state.entries[i].kind === 'anonymous') {
|
|
1583
|
+
return i;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
return -1;
|
|
1587
|
+
}
|
|
1588
|
+
function ensureEntry(state, targetElement, options = {}) {
|
|
1589
|
+
const index = findEntryIndex(state, targetElement, options.entryKey);
|
|
1590
|
+
const kind = targetElement ? 'element' : 'anonymous';
|
|
1591
|
+
if (index >= 0) {
|
|
1592
|
+
const entry = state.entries[index];
|
|
1593
|
+
entry.kind = kind;
|
|
1594
|
+
entry.element = targetElement ?? null;
|
|
1595
|
+
entry.reserveScrollBarGap = Boolean(options.reserveScrollBarGap);
|
|
1596
|
+
entry.allowTouchMove = options.allowTouchMove;
|
|
1597
|
+
return entry;
|
|
1598
|
+
}
|
|
1599
|
+
const entry = {
|
|
1600
|
+
key: options.entryKey ?? Symbol('scroll-lock-entry'),
|
|
1601
|
+
kind,
|
|
1602
|
+
element: targetElement ?? null,
|
|
1603
|
+
active: true,
|
|
1604
|
+
reserveScrollBarGap: Boolean(options.reserveScrollBarGap),
|
|
1605
|
+
allowTouchMove: options.allowTouchMove,
|
|
1606
|
+
};
|
|
1607
|
+
state.entries.push(entry);
|
|
1608
|
+
return entry;
|
|
1609
|
+
}
|
|
1610
|
+
function shouldPreventTouchMove(e, state, win) {
|
|
1611
|
+
if (e.touches.length > 1)
|
|
1612
|
+
return false;
|
|
1613
|
+
if (e.ctrlKey)
|
|
1614
|
+
return false;
|
|
1615
|
+
if (!e.cancelable)
|
|
1616
|
+
return false;
|
|
1617
|
+
const touch = e.touches[0];
|
|
1618
|
+
if (!touch)
|
|
1619
|
+
return false;
|
|
1620
|
+
const dy = touch.clientY - state.touchStartY;
|
|
1621
|
+
const dx = touch.clientX - state.touchStartX;
|
|
1622
|
+
if (dy === 0 && dx === 0)
|
|
1623
|
+
return false;
|
|
1624
|
+
if (getActiveEntries(state).length === 0)
|
|
1625
|
+
return false;
|
|
1626
|
+
const target = e.target;
|
|
1627
|
+
if (!(target instanceof win.Element)) {
|
|
1628
|
+
return true;
|
|
1629
|
+
}
|
|
1630
|
+
for (let i = state.entries.length - 1; i >= 0; i--) {
|
|
1631
|
+
if (canEntryAllowInnerScroll(state.entries[i], target, win, dx, dy)) {
|
|
1632
|
+
return false;
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
return true;
|
|
1636
|
+
}
|
|
1637
|
+
function lockIOS(state, win) {
|
|
1638
|
+
const doc = win.document;
|
|
1639
|
+
state.docTouchStartHandler = (e) => {
|
|
1640
|
+
if (e.touches.length > 0) {
|
|
1641
|
+
state.touchStartY = e.touches[0].clientY;
|
|
1642
|
+
state.touchStartX = e.touches[0].clientX;
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
state.docTouchMoveHandler = (e) => {
|
|
1646
|
+
if (shouldPreventTouchMove(e, state, win)) {
|
|
1647
|
+
e.preventDefault();
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
doc.addEventListener('touchstart', state.docTouchStartHandler, {
|
|
1651
|
+
passive: true,
|
|
1652
|
+
});
|
|
1653
|
+
doc.addEventListener('touchmove', state.docTouchMoveHandler, {
|
|
1654
|
+
passive: false,
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
function unlockIOS(state, win) {
|
|
1658
|
+
const doc = win.document;
|
|
1659
|
+
if (state.docTouchStartHandler) {
|
|
1660
|
+
doc.removeEventListener('touchstart', state.docTouchStartHandler);
|
|
1661
|
+
state.docTouchStartHandler = null;
|
|
1662
|
+
}
|
|
1663
|
+
if (state.docTouchMoveHandler) {
|
|
1664
|
+
doc.removeEventListener('touchmove', state.docTouchMoveHandler);
|
|
1665
|
+
state.docTouchMoveHandler = null;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
function lockNonIOS(state, win, { reserveScrollBarGap = false }) {
|
|
1669
|
+
const body = win.document.body;
|
|
1670
|
+
const gap = win.innerWidth - win.document.documentElement.clientWidth;
|
|
1671
|
+
state.savedBodyOverflow = body.style.overflow;
|
|
1672
|
+
if (reserveScrollBarGap && gap > 0) {
|
|
1673
|
+
const computedPaddingRight = parseInt(win.getComputedStyle(body).paddingRight, 10) || 0;
|
|
1674
|
+
state.savedBodyPaddingRight = body.style.paddingRight;
|
|
1675
|
+
body.style.paddingRight = `${computedPaddingRight + gap}px`;
|
|
1676
|
+
}
|
|
1677
|
+
body.style.overflow = 'hidden';
|
|
1678
|
+
}
|
|
1679
|
+
function unlockNonIOS(state, win) {
|
|
1680
|
+
const body = win.document.body;
|
|
1681
|
+
body.style.overflow = state.savedBodyOverflow;
|
|
1682
|
+
if (state.savedBodyPaddingRight !== null) {
|
|
1683
|
+
if (state.savedBodyPaddingRight === '') {
|
|
1684
|
+
body.style.removeProperty('padding-right');
|
|
1685
|
+
}
|
|
1686
|
+
else {
|
|
1687
|
+
body.style.paddingRight = state.savedBodyPaddingRight;
|
|
1688
|
+
}
|
|
1689
|
+
state.savedBodyPaddingRight = null;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
function bodyLockDesired(state) {
|
|
1693
|
+
return getActiveEntries(state).length > 0;
|
|
1694
|
+
}
|
|
1695
|
+
function shouldReserveScrollBarGap(state) {
|
|
1696
|
+
return state.entries.some((entry) => entry.active && entry.reserveScrollBarGap);
|
|
1697
|
+
}
|
|
1698
|
+
function syncIOS(state, win) {
|
|
1699
|
+
const desired = state.entries.length > 0;
|
|
1700
|
+
const attached = state.docTouchMoveHandler !== null;
|
|
1701
|
+
if (desired === attached)
|
|
1702
|
+
return;
|
|
1703
|
+
if (desired) {
|
|
1704
|
+
lockIOS(state, win);
|
|
1705
|
+
}
|
|
1706
|
+
else {
|
|
1707
|
+
unlockIOS(state, win);
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
function syncNonIOS(state, win) {
|
|
1711
|
+
const desired = bodyLockDesired(state);
|
|
1712
|
+
const reserveScrollBarGap = shouldReserveScrollBarGap(state);
|
|
1713
|
+
if (!desired) {
|
|
1714
|
+
if (!state.nonIOSLocked)
|
|
1715
|
+
return;
|
|
1716
|
+
unlockNonIOS(state, win);
|
|
1717
|
+
state.nonIOSLocked = false;
|
|
1718
|
+
state.nonIOSReservedScrollBarGap = false;
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
if (!state.nonIOSLocked) {
|
|
1722
|
+
lockNonIOS(state, win, {
|
|
1723
|
+
reserveScrollBarGap,
|
|
1724
|
+
});
|
|
1725
|
+
state.nonIOSLocked = true;
|
|
1726
|
+
state.nonIOSReservedScrollBarGap = reserveScrollBarGap;
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
if (reserveScrollBarGap === state.nonIOSReservedScrollBarGap)
|
|
1730
|
+
return;
|
|
1731
|
+
unlockNonIOS(state, win);
|
|
1732
|
+
lockNonIOS(state, win, { reserveScrollBarGap });
|
|
1733
|
+
state.nonIOSReservedScrollBarGap = reserveScrollBarGap;
|
|
1734
|
+
}
|
|
1735
|
+
function syncScrollLock(state, win) {
|
|
1736
|
+
if (isIOS(win)) {
|
|
1737
|
+
syncIOS(state, win);
|
|
1738
|
+
}
|
|
1739
|
+
else {
|
|
1740
|
+
syncNonIOS(state, win);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
function activateEntry(state, win, targetElement, options = {}) {
|
|
1744
|
+
const entry = ensureEntry(state, targetElement, options);
|
|
1745
|
+
entry.active = true;
|
|
1746
|
+
syncScrollLock(state, win);
|
|
1747
|
+
}
|
|
1748
|
+
function lock(state, win, targetElement, options = {}) {
|
|
1749
|
+
activateEntry(state, win, targetElement, options);
|
|
1750
|
+
}
|
|
1751
|
+
function unlock(state, win, targetElement, options = {}) {
|
|
1752
|
+
const index = findEntryIndex(state, targetElement, options.entryKey);
|
|
1753
|
+
if (index < 0)
|
|
1754
|
+
return;
|
|
1755
|
+
state.entries.splice(index, 1);
|
|
1756
|
+
syncScrollLock(state, win);
|
|
1757
|
+
}
|
|
1758
|
+
function pause(state, win, targetElement, options = {}) {
|
|
1759
|
+
const index = findEntryIndex(state, targetElement, options.entryKey);
|
|
1760
|
+
if (index < 0 || state.entries[index].active === false)
|
|
1761
|
+
return;
|
|
1762
|
+
state.entries[index].active = false;
|
|
1763
|
+
syncScrollLock(state, win);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
const SCROLL_LOCK_KEY = '__SUI_SCROLL_LOCK__';
|
|
1767
|
+
function getStoredScrollLockState(win) {
|
|
1768
|
+
return win[SCROLL_LOCK_KEY];
|
|
1769
|
+
}
|
|
1770
|
+
function setStoredScrollLockState(win, state) {
|
|
1771
|
+
const global = win;
|
|
1772
|
+
global[SCROLL_LOCK_KEY] = state;
|
|
1773
|
+
return state;
|
|
1774
|
+
}
|
|
1775
|
+
function getOrCreateScrollLockState(win) {
|
|
1776
|
+
const state = getStoredScrollLockState(win);
|
|
1777
|
+
if (!state) {
|
|
1778
|
+
return setStoredScrollLockState(win, createInitialState());
|
|
1779
|
+
}
|
|
1780
|
+
return state;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
function applyLockState(state, win, targetElement, options, disabled) {
|
|
1784
|
+
lock(state, win, targetElement, options);
|
|
1785
|
+
if (disabled) {
|
|
1786
|
+
pause(state, win, targetElement, options);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
function useResolvedElement(target) {
|
|
1790
|
+
const [resolvedElement, setResolvedElement] = useState(() => (getRefElement(target) ?? null));
|
|
1791
|
+
const targetRef = useRef(target);
|
|
1792
|
+
targetRef.current = target;
|
|
1793
|
+
useEffect(() => {
|
|
1794
|
+
const nextResolvedElement = (getRefElement(targetRef.current) ??
|
|
1795
|
+
null);
|
|
1796
|
+
setResolvedElement((current) => current === nextResolvedElement ? current : nextResolvedElement);
|
|
1797
|
+
});
|
|
1798
|
+
return resolvedElement;
|
|
1799
|
+
}
|
|
1448
1800
|
const useBodyScrollLock = (isLocked, targetElement, options = {}) => {
|
|
1449
|
-
const { disabled = false, reserveScrollBarGap = true
|
|
1450
|
-
const {
|
|
1801
|
+
const { disabled = false, reserveScrollBarGap = true } = options;
|
|
1802
|
+
const { allowTouchMove } = options;
|
|
1803
|
+
const entryKeyRef = useRef(Symbol('use-body-scroll-lock-entry'));
|
|
1804
|
+
const resolvedElement = useResolvedElement(targetElement);
|
|
1805
|
+
const { globalThis: global } = useWindowContext();
|
|
1451
1806
|
useEffect(() => {
|
|
1452
|
-
if (
|
|
1807
|
+
if (!isLocked)
|
|
1453
1808
|
return;
|
|
1454
|
-
const
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1809
|
+
const win = global;
|
|
1810
|
+
const state = getOrCreateScrollLockState(global);
|
|
1811
|
+
const entryKey = entryKeyRef.current;
|
|
1812
|
+
const syncOptions = {
|
|
1813
|
+
reserveScrollBarGap,
|
|
1814
|
+
allowTouchMove,
|
|
1815
|
+
entryKey,
|
|
1816
|
+
};
|
|
1817
|
+
applyLockState(state, win, resolvedElement, syncOptions, disabled);
|
|
1461
1818
|
return () => {
|
|
1462
|
-
|
|
1463
|
-
|
|
1819
|
+
const currentState = getStoredScrollLockState(global);
|
|
1820
|
+
if (currentState) {
|
|
1821
|
+
unlock(currentState, win, resolvedElement, { entryKey });
|
|
1464
1822
|
}
|
|
1465
1823
|
};
|
|
1824
|
+
}, [isLocked, global]);
|
|
1825
|
+
useEffect(() => {
|
|
1826
|
+
if (!isLocked)
|
|
1827
|
+
return;
|
|
1828
|
+
const win = global;
|
|
1829
|
+
const state = getOrCreateScrollLockState(global);
|
|
1830
|
+
const entryKey = entryKeyRef.current;
|
|
1831
|
+
const syncOptions = {
|
|
1832
|
+
reserveScrollBarGap,
|
|
1833
|
+
allowTouchMove,
|
|
1834
|
+
entryKey,
|
|
1835
|
+
};
|
|
1836
|
+
applyLockState(state, win, resolvedElement, syncOptions, disabled);
|
|
1466
1837
|
}, [
|
|
1467
|
-
|
|
1468
|
-
targetElement,
|
|
1838
|
+
allowTouchMove,
|
|
1469
1839
|
disabled,
|
|
1840
|
+
isLocked,
|
|
1841
|
+
global,
|
|
1842
|
+
resolvedElement,
|
|
1470
1843
|
reserveScrollBarGap,
|
|
1471
|
-
allowTouchMove,
|
|
1472
|
-
document,
|
|
1473
1844
|
]);
|
|
1474
1845
|
};
|
|
1475
1846
|
|
|
1476
|
-
|
|
1847
|
+
const useVirtuosoExternalWindow = () => {
|
|
1848
|
+
const { externalWindow } = useWindowContext();
|
|
1849
|
+
if (externalWindow && externalWindow !== globalThis.window) {
|
|
1850
|
+
return {
|
|
1851
|
+
externalWindow,
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
return undefined;
|
|
1855
|
+
};
|
|
1856
|
+
|
|
1857
|
+
export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useButton, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, useVirtuosoExternalWindow, visuallyHiddenStyles };
|
package/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
export { configure, rcConfiguration } from './config.js';
|
|
2
2
|
export { RcResponsiveContext, WindowContext, breakpointList, getMatchDetail, useResponsiveContext, useWindowContext } from './contexts.js';
|
|
3
|
-
export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, visuallyHiddenStyles } from './hooks.js';
|
|
3
|
+
export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useButton, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, useVirtuosoExternalWindow, visuallyHiddenStyles } from './hooks.js';
|
|
4
4
|
export { unstable_ownerDocument as ownerDocument, unstable_useControlled as useControlled } from '@mui/utils';
|
|
5
5
|
export { a11yKeyboard, capitalize, checkDefaultPrevented, checkElementName, clearWindowSelection, getSelectionPosition, getTitle, globalStateClasses, isDeleteKeyboardEvent, isSuiElement, isUrl, isWebKit154, omit, outerSize, pick, preloadImg, removeClassName, runKeyHandler, setSelectionPosition, useClasses } from './utils.js';
|
|
6
6
|
export { a as ariaHideOutside, b as combineClasses, c as combineProps, g as getRefElement, i as isRef, l as logInDev, o as ownerWindow, t as twMerge } from './chunks/twMerge.js';
|
|
7
7
|
import 'react';
|
|
8
8
|
import 'lodash/uniqueId';
|
|
9
|
-
import '
|
|
9
|
+
import '@mui/base/useButton';
|
|
10
10
|
import 'lodash/omit';
|
|
11
11
|
import 'lodash/pick';
|
|
12
|
+
import 'lodash/isPlainObject';
|
|
12
13
|
import 'lodash/debounce';
|
|
13
14
|
import 'lodash/noop';
|
|
14
15
|
import 'lodash/throttle';
|
|
15
|
-
import 'body-scroll-lock';
|
|
16
16
|
import 'tailwind-merge';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ringcentral/spring-base",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"author": "RingCentral",
|
|
5
5
|
"description": "Core utilities, hooks, and foundational functionality for Spring UI components, providing essential building blocks for React applications",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@mui/utils": "^6.0.2",
|
|
11
11
|
"@types/lodash": "^4.14.182",
|
|
12
|
-
"body-scroll-lock": "4.0.0-beta.0",
|
|
13
12
|
"clsx": "^1.2.1",
|
|
14
13
|
"framer-motion": "^6.5.1",
|
|
15
14
|
"lodash": "^4.17.21",
|
package/src/hooks/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './useA11yKeyEvent';
|
|
2
2
|
export * from './useActive';
|
|
3
3
|
export * from './useAnnouncer';
|
|
4
|
+
export * from './useButton';
|
|
4
5
|
export * from './useAriaHideOutside';
|
|
5
6
|
export * from './useAudio';
|
|
6
7
|
export * from './useChange';
|
|
@@ -41,3 +42,4 @@ export * from './useTimeout';
|
|
|
41
42
|
export * from './useTouchMouseEvent';
|
|
42
43
|
export * from './useFontLoaded';
|
|
43
44
|
export * from './useBodyScrollLock';
|
|
45
|
+
export * from './useVirtuosoExternalWindow';
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { EventHandler, KeyboardEventHandler, SyntheticEvent } from 'react';
|
|
1
|
+
import { EventHandler, KeyboardEventHandler, MouseEventHandler, SyntheticEvent } from 'react';
|
|
2
2
|
export declare type UseActiveOptions = {
|
|
3
3
|
onKeyDown?: KeyboardEventHandler;
|
|
4
4
|
onKeyUp?: KeyboardEventHandler;
|
|
5
|
+
onMouseDown?: MouseEventHandler;
|
|
5
6
|
onClick?: EventHandler<SyntheticEvent>;
|
|
6
7
|
disabled?: boolean;
|
|
7
8
|
};
|
|
8
|
-
export declare const useActive: ({ onKeyDown, onKeyUp, onClick, disabled, }?: UseActiveOptions) => {
|
|
9
|
+
export declare const useActive: ({ onKeyDown, onKeyUp, onMouseDown, onClick, disabled, }?: UseActiveOptions) => {
|
|
9
10
|
activeHandlers: {
|
|
10
11
|
onKeyDown: KeyboardEventHandler<Element> | undefined;
|
|
11
12
|
onKeyUp: KeyboardEventHandler<Element> | undefined;
|
|
13
|
+
onMouseDown: MouseEventHandler<Element> | undefined;
|
|
12
14
|
onClick: ((event: SyntheticEvent<Element, Event>) => void) | undefined;
|
|
13
15
|
};
|
|
14
16
|
active: boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
declare type Win = Window & typeof globalThis;
|
|
2
|
+
export interface ScrollLockOptions {
|
|
3
|
+
reserveScrollBarGap?: boolean;
|
|
4
|
+
allowTouchMove?: (el: Element) => boolean;
|
|
5
|
+
entryKey?: symbol;
|
|
6
|
+
}
|
|
7
|
+
interface LockEntry {
|
|
8
|
+
key: symbol;
|
|
9
|
+
kind: 'anonymous' | 'element';
|
|
10
|
+
element: HTMLElement | null;
|
|
11
|
+
active: boolean;
|
|
12
|
+
reserveScrollBarGap: boolean;
|
|
13
|
+
allowTouchMove?: (el: Element) => boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface WindowState {
|
|
16
|
+
entries: LockEntry[];
|
|
17
|
+
savedBodyOverflow: string;
|
|
18
|
+
savedBodyPaddingRight: string | null;
|
|
19
|
+
docTouchStartHandler: ((e: TouchEvent) => void) | null;
|
|
20
|
+
docTouchMoveHandler: ((e: TouchEvent) => void) | null;
|
|
21
|
+
touchStartY: number;
|
|
22
|
+
touchStartX: number;
|
|
23
|
+
nonIOSLocked: boolean;
|
|
24
|
+
nonIOSReservedScrollBarGap: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function createInitialState(): WindowState;
|
|
27
|
+
export declare function lock(state: WindowState, win: Win, targetElement?: HTMLElement | null, options?: ScrollLockOptions): void;
|
|
28
|
+
export declare function unlock(state: WindowState, win: Win, targetElement?: HTMLElement | null, options?: ScrollLockOptions): void;
|
|
29
|
+
export declare function pause(state: WindowState, win: Win, targetElement?: HTMLElement | null, options?: ScrollLockOptions): void;
|
|
30
|
+
export declare function clearAllLocks(state: WindowState, win: Win): void;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { WindowState } from './bodyScrollLock';
|
|
2
|
+
export declare const SCROLL_LOCK_KEY = "__SUI_SCROLL_LOCK__";
|
|
3
|
+
export declare function getStoredScrollLockState(win: typeof globalThis): WindowState | undefined;
|
|
4
|
+
export declare function setStoredScrollLockState(win: typeof globalThis, state: WindowState): WindowState;
|
|
5
|
+
export declare function getOrCreateScrollLockState(win: typeof globalThis): WindowState;
|
|
6
|
+
export declare function deleteStoredScrollLockState(win: typeof globalThis): void;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export declare type UseBodyScrollLockTarget = HTMLElement | RefObject<HTMLElement | null> | null | undefined;
|
|
1
3
|
export interface UseBodyScrollLockOptions {
|
|
2
4
|
disabled?: boolean;
|
|
3
5
|
reserveScrollBarGap?: boolean;
|
|
4
6
|
allowTouchMove?: (el: Element) => boolean;
|
|
5
7
|
}
|
|
6
|
-
export declare const useBodyScrollLock: (isLocked: boolean, targetElement?:
|
|
8
|
+
export declare const useBodyScrollLock: (isLocked: boolean, targetElement?: UseBodyScrollLockTarget, options?: UseBodyScrollLockOptions) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useButton } from './useButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useVirtuosoExternalWindow';
|
|
@@ -7,15 +7,16 @@ export declare type GroupedMotionProps<P = {}> = Pick<MotionProps, Extract<keyof
|
|
|
7
7
|
export declare type ClassesProp<K extends string> = {
|
|
8
8
|
classes?: Partial<Record<K | 'root' | GlobalStateClassesKey, string>>;
|
|
9
9
|
};
|
|
10
|
-
export declare type PropsWithRoot<P, R extends ElementType, O extends string = ''> = P & Omit<ComponentPropsWithRef<R>, keyof P | O
|
|
11
|
-
export declare type
|
|
10
|
+
export declare type PropsWithRoot<P, R extends ElementType, O extends string = '', E = HTMLDataAttribute> = P & Omit<ComponentPropsWithRef<R>, keyof P | O> & E;
|
|
11
|
+
export declare type SlotProps<R extends ElementType, O extends keyof ComponentPropsWithRef<R> = never, E = HTMLDataAttribute> = Omit<ComponentPropsWithRef<R>, O | 'className'> & E;
|
|
12
|
+
export declare type StandardProps<P, C extends string, R extends ElementType | void = void, O extends string = '', ExtendAttributes = HTMLDataAttribute> = ClassesProp<C> & (R extends ElementType ? PropsWithRoot<P, R, 'classes' | O, ExtendAttributes> : P & {
|
|
12
13
|
className?: string;
|
|
13
14
|
}) & ExtendAttributes;
|
|
14
15
|
export declare type WithClassesProps<P, C extends string> = P & ClassesProp<C>;
|
|
15
16
|
export declare type BaseOverridableProps<P, C extends string> = WithClassesProps<P, C>;
|
|
16
|
-
export declare type OverridableProps<B, T extends ElementType> = B & {
|
|
17
|
+
export declare type OverridableProps<B, T extends ElementType, E = HTMLDataAttribute> = B & {
|
|
17
18
|
component?: T;
|
|
18
|
-
} & Omit<ComponentPropsWithRef<T>, keyof B
|
|
19
|
+
} & Omit<ComponentPropsWithRef<T>, keyof B> & E;
|
|
19
20
|
export declare type RefFromProps<P> = P extends {
|
|
20
21
|
ref?: LegacyRef<infer R> | undefined;
|
|
21
22
|
} ? R : never;
|