@foxford/ui 2.126.0 → 2.127.0
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/components/Dialog/Dialog.js +1 -1
- package/components/Dialog/Dialog.js.map +1 -1
- package/components/Dialog/Dialog.mjs +1 -1
- package/components/Dialog/Dialog.mjs.map +1 -1
- package/components/Dialog/style.js +1 -1
- package/components/Dialog/style.js.map +1 -1
- package/components/Dialog/style.mjs +1 -1
- package/components/Dialog/style.mjs.map +1 -1
- package/components/DialogComponent/Body.js +2 -0
- package/components/DialogComponent/Body.js.map +1 -0
- package/components/DialogComponent/Body.mjs +2 -0
- package/components/DialogComponent/Body.mjs.map +1 -0
- package/components/DialogComponent/DialogComponent.js +1 -1
- package/components/DialogComponent/DialogComponent.js.map +1 -1
- package/components/DialogComponent/DialogComponent.mjs +1 -1
- package/components/DialogComponent/DialogComponent.mjs.map +1 -1
- package/components/DialogComponent/DraggingHandle.js +2 -0
- package/components/DialogComponent/DraggingHandle.js.map +1 -0
- package/components/DialogComponent/DraggingHandle.mjs +2 -0
- package/components/DialogComponent/DraggingHandle.mjs.map +1 -0
- package/components/DialogComponent/MediaBlock.js +2 -0
- package/components/DialogComponent/MediaBlock.js.map +1 -0
- package/components/DialogComponent/MediaBlock.mjs +2 -0
- package/components/DialogComponent/MediaBlock.mjs.map +1 -0
- package/components/DialogComponent/layouts.js +1 -1
- package/components/DialogComponent/layouts.js.map +1 -1
- package/components/DialogComponent/layouts.mjs +1 -1
- package/components/DialogComponent/layouts.mjs.map +1 -1
- package/components/DialogComponent/sizes.js +1 -1
- package/components/DialogComponent/sizes.js.map +1 -1
- package/components/DialogComponent/sizes.mjs +1 -1
- package/components/DialogComponent/sizes.mjs.map +1 -1
- package/components/DialogComponent/style.js +1 -1
- package/components/DialogComponent/style.js.map +1 -1
- package/components/DialogComponent/style.mjs +1 -1
- package/components/DialogComponent/style.mjs.map +1 -1
- package/dts/index.d.ts +70 -36
- package/hooks/useScrollLock.js +1 -1
- package/hooks/useScrollLock.js.map +1 -1
- package/hooks/useScrollLock.mjs +1 -1
- package/hooks/useScrollLock.mjs.map +1 -1
- package/package.json +2 -2
package/dts/index.d.ts
CHANGED
|
@@ -5538,6 +5538,41 @@ declare const Menu: React.ForwardRefExoticComponent<MenuProps> & {
|
|
|
5538
5538
|
Divider: typeof MenuDivider;
|
|
5539
5539
|
};
|
|
5540
5540
|
|
|
5541
|
+
interface AspectRatioBaseProps {
|
|
5542
|
+
/**
|
|
5543
|
+
* Целевой элемент.
|
|
5544
|
+
*/
|
|
5545
|
+
children: React.ReactNode;
|
|
5546
|
+
/**
|
|
5547
|
+
* Соотношение сторон.
|
|
5548
|
+
*
|
|
5549
|
+
* @default 1:1 */
|
|
5550
|
+
ratio?: `${number}:${number}`;
|
|
5551
|
+
/**
|
|
5552
|
+
* Ширина контейнера (высота будет рассчитана согласно ratio).
|
|
5553
|
+
*
|
|
5554
|
+
* Для корретной работы нужно передать ширину или высоту.
|
|
5555
|
+
*
|
|
5556
|
+
* @default min-content */
|
|
5557
|
+
width?: React.CSSProperties['width'];
|
|
5558
|
+
/**
|
|
5559
|
+
* Высота контейнера (ширина будет рассчитана согласно ratio).
|
|
5560
|
+
*
|
|
5561
|
+
* Для корретной работы нужно передать ширину или высоту.
|
|
5562
|
+
*
|
|
5563
|
+
* @default min-content */
|
|
5564
|
+
height?: React.CSSProperties['height'];
|
|
5565
|
+
}
|
|
5566
|
+
type AspectRatioProps = AspectRatioBaseProps & Omit<React.ComponentPropsWithRef<'div'>, keyof AspectRatioBaseProps>;
|
|
5567
|
+
|
|
5568
|
+
/**
|
|
5569
|
+
*
|
|
5570
|
+
* Компонент для создания плейсхолдеров с определенным соотношением сторон.
|
|
5571
|
+
*
|
|
5572
|
+
* Поддерживается "ref" и все нативные атрибуты \<div\> элемента.
|
|
5573
|
+
*/
|
|
5574
|
+
declare const AspectRatio: React.ForwardRefExoticComponent<AspectRatioProps>;
|
|
5575
|
+
|
|
5541
5576
|
type Size$d = 'xxl' | 'xl' | 'l' | 'm' | 's' | 'xs';
|
|
5542
5577
|
type Layout$3 = 'vertical' | 'horizontal' | 'vertical-reverse' | 'horizontal-reverse';
|
|
5543
5578
|
type DialogComponentPalette = {
|
|
@@ -5697,9 +5732,15 @@ interface DialogComponentBaseProps extends ResponsiveSizeProps<Size$d>, Responsi
|
|
|
5697
5732
|
/**
|
|
5698
5733
|
* Определяет, должен ли хедер быть фиксированным при прокрутке контента.
|
|
5699
5734
|
*
|
|
5700
|
-
* @general
|
|
5701
5735
|
* @default false */
|
|
5702
5736
|
stickyHeader?: boolean;
|
|
5737
|
+
/**
|
|
5738
|
+
* Резервировать место под скроллбар для скроллируемого контента.
|
|
5739
|
+
*
|
|
5740
|
+
* Применяется при включенном scrollable.
|
|
5741
|
+
*
|
|
5742
|
+
* @default false */
|
|
5743
|
+
scrollAreaReserveScrollbarGutter?: boolean;
|
|
5703
5744
|
/**
|
|
5704
5745
|
* Колбек будет передан в onPointerDown элемента области захвата.
|
|
5705
5746
|
*
|
|
@@ -5710,6 +5751,11 @@ interface DialogComponentBaseProps extends ResponsiveSizeProps<Size$d>, Responsi
|
|
|
5710
5751
|
*
|
|
5711
5752
|
* @default undefined */
|
|
5712
5753
|
onNudge?: React.KeyboardEventHandler<HTMLElement>;
|
|
5754
|
+
/**
|
|
5755
|
+
* Колбек, который получает аргументом скроллируемый элемент контента.
|
|
5756
|
+
*
|
|
5757
|
+
* @default undefined */
|
|
5758
|
+
scrollAreaRef?: (ref: HTMLDivElement | null) => void;
|
|
5713
5759
|
}
|
|
5714
5760
|
type DialogComponentProps = DialogComponentBaseProps & Omit<React.ComponentPropsWithRef<'div'>, keyof DialogComponentBaseProps>;
|
|
5715
5761
|
|
|
@@ -6013,6 +6059,11 @@ interface DialogBaseProps extends ResponsiveSizeProps<Size$c>, ResponsiveLayoutP
|
|
|
6013
6059
|
* @general
|
|
6014
6060
|
* @default false */
|
|
6015
6061
|
stickyHeader?: boolean;
|
|
6062
|
+
/**
|
|
6063
|
+
* Колбек, который получает аргументом скроллируемый элемент контента.
|
|
6064
|
+
*
|
|
6065
|
+
* @default undefined */
|
|
6066
|
+
scrollAreaRef?: (ref: HTMLDivElement | null) => void;
|
|
6016
6067
|
/**
|
|
6017
6068
|
* Параметры блокировки скролла.
|
|
6018
6069
|
*
|
|
@@ -6027,7 +6078,19 @@ interface DialogBaseProps extends ResponsiveSizeProps<Size$c>, ResponsiveLayoutP
|
|
|
6027
6078
|
* Контейнер, в котором разрешена прокрутка на iOS при заблокированном скролле страницы.
|
|
6028
6079
|
*/
|
|
6029
6080
|
iosAllowedScrollContainer?: HTMLElement;
|
|
6081
|
+
/**
|
|
6082
|
+
* Резервировать место под скроллбар (добавляет padding-right или scrollbar-gutter: stable).
|
|
6083
|
+
*/
|
|
6084
|
+
reserveScrollbarGutter?: boolean;
|
|
6030
6085
|
};
|
|
6086
|
+
/**
|
|
6087
|
+
* Резервировать место под скроллбар для скроллируемого контента.
|
|
6088
|
+
*
|
|
6089
|
+
* Применяется при включенном scrollable.
|
|
6090
|
+
*
|
|
6091
|
+
* @general
|
|
6092
|
+
* @default false */
|
|
6093
|
+
scrollAreaReserveScrollbarGutter?: boolean;
|
|
6031
6094
|
/**
|
|
6032
6095
|
* Размер.
|
|
6033
6096
|
*
|
|
@@ -6158,6 +6221,8 @@ interface DialogBaseProps extends ResponsiveSizeProps<Size$c>, ResponsiveLayoutP
|
|
|
6158
6221
|
type DialogProps = DialogBaseProps & Omit<React.ComponentPropsWithRef<'div'>, keyof DialogBaseProps>;
|
|
6159
6222
|
|
|
6160
6223
|
/**
|
|
6224
|
+
*
|
|
6225
|
+
* *v2.0.0*
|
|
6161
6226
|
*
|
|
6162
6227
|
* Компонент для отображения контента поверх основного интерфейса.
|
|
6163
6228
|
*
|
|
@@ -7677,6 +7742,10 @@ type ScrollLockParams = {
|
|
|
7677
7742
|
* @default 'document.documentElement'
|
|
7678
7743
|
*/
|
|
7679
7744
|
target?: DomTarget;
|
|
7745
|
+
/**
|
|
7746
|
+
* Резервировать место под скроллбар (добавляет padding-right или scrollbar-gutter: stable).
|
|
7747
|
+
*/
|
|
7748
|
+
reserveScrollbarGutter?: boolean;
|
|
7680
7749
|
};
|
|
7681
7750
|
declare function useScrollLock(params: ScrollLockParams): void;
|
|
7682
7751
|
|
|
@@ -10243,41 +10312,6 @@ type RenderOptionListItemProps = ListItemProps & {
|
|
|
10243
10312
|
*/
|
|
10244
10313
|
declare const Dropdown: React.ForwardRefExoticComponent<DropdownProps>;
|
|
10245
10314
|
|
|
10246
|
-
interface AspectRatioBaseProps {
|
|
10247
|
-
/**
|
|
10248
|
-
* Целевой элемент.
|
|
10249
|
-
*/
|
|
10250
|
-
children: React.ReactNode;
|
|
10251
|
-
/**
|
|
10252
|
-
* Соотношение сторон.
|
|
10253
|
-
*
|
|
10254
|
-
* @default 1:1 */
|
|
10255
|
-
ratio?: `${number}:${number}`;
|
|
10256
|
-
/**
|
|
10257
|
-
* Ширина контейнера (высота будет рассчитана согласно ratio).
|
|
10258
|
-
*
|
|
10259
|
-
* Для корретной работы нужно передать ширину или высоту.
|
|
10260
|
-
*
|
|
10261
|
-
* @default min-content */
|
|
10262
|
-
width?: React.CSSProperties['width'];
|
|
10263
|
-
/**
|
|
10264
|
-
* Высота контейнера (ширина будет рассчитана согласно ratio).
|
|
10265
|
-
*
|
|
10266
|
-
* Для корретной работы нужно передать ширину или высоту.
|
|
10267
|
-
*
|
|
10268
|
-
* @default min-content */
|
|
10269
|
-
height?: React.CSSProperties['height'];
|
|
10270
|
-
}
|
|
10271
|
-
type AspectRatioProps = AspectRatioBaseProps & Omit<React.ComponentPropsWithRef<'div'>, keyof AspectRatioBaseProps>;
|
|
10272
|
-
|
|
10273
|
-
/**
|
|
10274
|
-
*
|
|
10275
|
-
* Компонент для создания плейсхолдеров с определенным соотношением сторон.
|
|
10276
|
-
*
|
|
10277
|
-
* Поддерживается "ref" и все нативные атрибуты \<div\> элемента.
|
|
10278
|
-
*/
|
|
10279
|
-
declare const AspectRatio: React.ForwardRefExoticComponent<AspectRatioProps>;
|
|
10280
|
-
|
|
10281
10315
|
type Size$1 = 'l' | 'm' | 's';
|
|
10282
10316
|
type Margin$5 = number | string;
|
|
10283
10317
|
interface AddElementBaseProps extends ResponsiveSizeProps<Size$1, SizeValue$1>, ResponsiveMarginProps {
|
package/hooks/useScrollLock.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var React=require('react');var getDomTargets=require('../shared/utils/getDomTargets.js');const TOUCH_EVENT_OPTIONS={capture:!0,passive:!1};const supportsScrollbarGutter=typeof CSS!='undefined'&&typeof CSS.supports=='function'&&CSS.supports('scrollbar-gutter: stable');const isIosDevice=typeof window!='undefined'&&window.navigator?.userAgent&&(/iP(ad|hone|od)/.test(window.navigator.userAgent)||window.navigator.userAgent.includes('Macintosh')&&window.navigator.maxTouchPoints>1);const iosScrollableBlocks=[];let iosActiveScrollElement=null;let iosTouchStartY=null;let touchStartHandler=null;let touchMoveHandler=null;function getScrollLockCount(){return Number(document.documentElement.dataset.scrollLockCount||0)}function setScrollLockCount(t){document.documentElement.dataset.scrollLockCount=t.toString()}function applyScrollLockStyles(
|
|
1
|
+
'use strict';var React=require('react');var getDomTargets=require('../shared/utils/getDomTargets.js');const TOUCH_EVENT_OPTIONS={capture:!0,passive:!1};const supportsScrollbarGutter=typeof CSS!='undefined'&&typeof CSS.supports=='function'&&CSS.supports('scrollbar-gutter: stable');const isIosDevice=typeof window!='undefined'&&window.navigator?.userAgent&&(/iP(ad|hone|od)/.test(window.navigator.userAgent)||window.navigator.userAgent.includes('Macintosh')&&window.navigator.maxTouchPoints>1);const iosScrollableBlocks=[];let iosActiveScrollElement=null;let iosTouchStartY=null;let touchStartHandler=null;let touchMoveHandler=null;function getScrollLockCount(){return Number(document.documentElement.dataset.scrollLockCount||0)}function setScrollLockCount(t){document.documentElement.dataset.scrollLockCount=t.toString()}function applyScrollLockStyles(t,e=!0,o=document.documentElement){e&&o.dataset.prevOverflow===void 0&&(o.dataset.prevOverflow=o.style.overflow||''),t&&(t=>{if(supportsScrollbarGutter&&!isIosDevice)t.style.scrollbarGutter='stable';else{const e=getComputedStyle(t);const o=(t=>{if(t===document.documentElement)return window.innerWidth-document.documentElement.clientWidth;const e=getComputedStyle(t);const o=(parseFloat(e.borderLeftWidth)||0)+(parseFloat(e.borderRightWidth)||0);const l=t.style.overflow;t.style.overflow='scroll';const n=t.offsetWidth-t.clientWidth-o;return t.style.overflow=l,n})(t);if(t.dataset.prevPaddingRight===void 0&&(t.dataset.prevPaddingRight=t.style.paddingRight||''),o>0){const l=parseFloat(e.paddingRight)||0;t.style.paddingRight=`${l+o}px`}}})(o),e&&(o.style.overflow='hidden')}function resetScrollLockStyles(t=document.documentElement){t.dataset.prevOverflow!==void 0&&(t.style.overflow=t.dataset.prevOverflow),t.dataset.prevPaddingRight!==void 0&&(t.style.paddingRight=t.dataset.prevPaddingRight),delete t.dataset.prevOverflow,delete t.dataset.prevPaddingRight,supportsScrollbarGutter&&(t.style.scrollbarGutter='')}function unlockScroll(){const t=getScrollLockCount();if(t<=0)return;const e=t-1;setScrollLockCount(e),e===0&&resetScrollLockStyles()}function isScrollable(t){const e=getComputedStyle(t);return(e.overflowY==='auto'||e.overflowY==='scroll')&&t.scrollHeight>t.clientHeight}function findScrollableParent(t,e){let o=t;for(;o&&o!==e.parentElement;){if(isScrollable(o))return o;o=o.parentElement}return isScrollable(e)?e:null}function unlockScrollIOS(){iosScrollableBlocks.pop();const t=getScrollLockCount();const e=Math.max(0,t-1);setScrollLockCount(e),e===0&&(resetScrollLockStyles(),touchStartHandler&&(document.removeEventListener('touchstart',touchStartHandler,TOUCH_EVENT_OPTIONS),touchStartHandler=null),touchMoveHandler&&(document.removeEventListener('touchmove',touchMoveHandler,TOUCH_EVENT_OPTIONS),touchMoveHandler=null),iosActiveScrollElement=null,iosTouchStartY=null)}exports.useScrollLock=t=>{const{locked:e,iosAllowedScrollContainer:o=document.documentElement,target:l,reserveScrollbarGutter:n=!1}=t;const r=l===void 0||l===document.documentElement||l===document.body;React.useEffect(()=>{if(!l||r)return;const t=getDomTargets.getDomTargets(l)[0];return t?((t,e,o)=>(applyScrollLockStyles(o,e,t),()=>resetScrollLockStyles(t)))(t,e,n):void 0},[l,e,r,n]),React.useEffect(()=>{if(e&&r){if(isIosDevice){const t=getDomTargets.getDomTargets(o)[0];return((t,e)=>{e&&iosScrollableBlocks.push(e);const o=getScrollLockCount();return o===0&&(applyScrollLockStyles(t),touchMoveHandler||touchStartHandler||(touchStartHandler=t=>{const e=t.target;if(!(e instanceof HTMLElement))return iosActiveScrollElement=null,void(iosTouchStartY=null);iosTouchStartY=t.touches[0]?.clientY??null,iosActiveScrollElement=null;for(const t of iosScrollableBlocks)if(t.contains(e)){const o=findScrollableParent(e,t);if(o){iosActiveScrollElement=o;break}}},touchMoveHandler=t=>{const e=t.touches[0];if(!e||t.touches.length===2||(t=>{if(!(t&&t instanceof HTMLElement))return!1;const e=t.ownerDocument?.defaultView?.getSelection();return!(!e||e.isCollapsed||!e.containsNode(t,!0))||'selectionStart'in t&&'selectionEnd'in t&&Number(t.selectionStart)<Number(t.selectionEnd)&&t.ownerDocument?.activeElement===t})(t.target))return;if(!t.cancelable)return;if(!iosActiveScrollElement||iosTouchStartY===null)return void t.preventDefault();const o=e.clientY-Number(iosTouchStartY);(iosActiveScrollElement.scrollTop<=0&&o>0||iosActiveScrollElement.scrollTop+iosActiveScrollElement.clientHeight>=iosActiveScrollElement.scrollHeight&&o<0)&&t.preventDefault()},document.addEventListener('touchstart',touchStartHandler,TOUCH_EVENT_OPTIONS),document.addEventListener('touchmove',touchMoveHandler,TOUCH_EVENT_OPTIONS))),setScrollLockCount(o+1),unlockScrollIOS})(n,t)}return(t=>{const e=getScrollLockCount();return e===0&&applyScrollLockStyles(t),setScrollLockCount(e+1),unlockScroll})(n)}},[o,r,e,n])};
|
|
2
2
|
//# sourceMappingURL=useScrollLock.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollLock.js","sources":["../../../src/hooks/useScrollLock.ts"],"sourcesContent":["import { useEffect } from 'react'\nimport type { DomTarget, Nullable } from 'shared/types'\nimport { getDomTargets } from 'shared/utils/getDomTargets'\n\ntype ScrollLockParams = {\n /**\n * Блокировать скролл.\n */\n locked: boolean\n /**\n * Контейнер, в котором разрешена прокрутка на iOS при заблокированном скролле страницы.\n */\n iosAllowedScrollContainer?: DomTarget\n /**\n * Элемент, на котором блокируется скролл.\n *\n * @default 'document.documentElement'\n */\n target?: DomTarget\n}\n\nconst TOUCH_EVENT_OPTIONS = { capture: true, passive: false }\nconst DATA_SCROLL_LOCK_COUNT = 'scrollLockCount'\nconst DATA_PREV_OVERFLOW = 'prevOverflow'\nconst DATA_PREV_PADDING_RIGHT = 'prevPaddingRight'\n\nconst supportsScrollbarGutter =\n typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('scrollbar-gutter: stable')\nconst isIosDevice =\n typeof window !== 'undefined' &&\n window.navigator?.userAgent &&\n (/iP(ad|hone|od)/.test(window.navigator.userAgent) ||\n (window.navigator.userAgent.includes('Macintosh') && window.navigator.maxTouchPoints > 1))\n\nconst iosScrollableBlocks: HTMLElement[] = []\nlet iosActiveScrollElement: Nullable<HTMLElement> = null\nlet iosTouchStartY: Nullable<number> = null\nlet touchStartHandler: Nullable<(event: TouchEvent) => void> = null\nlet touchMoveHandler: Nullable<(event: TouchEvent) => void> = null\n\nfunction getScrollLockCount(): number {\n return Number(document.documentElement.dataset[DATA_SCROLL_LOCK_COUNT] || 0)\n}\n\nfunction setScrollLockCount(count: number) {\n document.documentElement.dataset[DATA_SCROLL_LOCK_COUNT] = count.toString()\n}\n\nfunction applyScrollLockStyles() {\n const root = document.documentElement\n root.dataset[DATA_PREV_OVERFLOW] = root.style.overflow || ''\n\n if (supportsScrollbarGutter && !isIosDevice) {\n root.style.scrollbarGutter = 'stable'\n } else {\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth\n root.dataset[DATA_PREV_PADDING_RIGHT] = root.style.paddingRight || ''\n if (scrollbarWidth > 0) {\n const currentPaddingRight = parseFloat(getComputedStyle(root).paddingRight) || 0\n root.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`\n }\n }\n root.style.overflow = 'hidden'\n}\n\nfunction resetScrollLockStyles() {\n const root = document.documentElement\n root.style.overflow = root.dataset[DATA_PREV_OVERFLOW] || ''\n root.style.paddingRight = root.dataset[DATA_PREV_PADDING_RIGHT] || ''\n delete root.dataset[DATA_PREV_OVERFLOW]\n delete root.dataset[DATA_PREV_PADDING_RIGHT]\n}\n\nfunction lockScroll() {\n const count = getScrollLockCount()\n if (count === 0) {\n applyScrollLockStyles()\n }\n setScrollLockCount(count + 1)\n}\n\nfunction unlockScroll() {\n const currentCount = getScrollLockCount()\n if (currentCount <= 0) {\n return\n }\n\n const nextCount = currentCount - 1\n setScrollLockCount(nextCount)\n if (nextCount === 0) {\n resetScrollLockStyles()\n }\n}\n\nfunction isScrollable(element: HTMLElement): boolean {\n const style = getComputedStyle(element)\n const overflowY = style.overflowY === 'auto' || style.overflowY === 'scroll'\n\n return overflowY && element.scrollHeight > element.clientHeight\n}\n\nfunction findScrollableParent(startElement: HTMLElement, root: HTMLElement): Nullable<HTMLElement> {\n let element: Nullable<HTMLElement> = startElement\n\n while (element && element !== root.parentElement) {\n if (isScrollable(element)) {\n return element\n }\n\n element = element.parentElement\n }\n\n if (isScrollable(root)) {\n return root\n }\n\n return null\n}\n\nfunction isTouchMoveAllowed(target: Nullable<EventTarget>): boolean {\n if (!target || !(target instanceof HTMLElement)) {\n return false\n }\n\n const selection = target.ownerDocument?.defaultView?.getSelection()\n if (selection && !selection.isCollapsed && selection.containsNode(target, true)) {\n return true\n }\n\n if (\n 'selectionStart' in target &&\n 'selectionEnd' in target &&\n Number(target.selectionStart) < Number(target.selectionEnd) &&\n target.ownerDocument?.activeElement === target\n ) {\n return true\n }\n\n return false\n}\n\nfunction addIOSEvents() {\n if (touchMoveHandler || touchStartHandler) {\n return\n }\n\n touchStartHandler = (event: TouchEvent) => {\n const target = event.target\n if (!(target instanceof HTMLElement)) {\n iosActiveScrollElement = null\n iosTouchStartY = null\n\n return\n }\n\n iosTouchStartY = event.touches[0]?.clientY ?? null\n iosActiveScrollElement = null\n for (const block of iosScrollableBlocks) {\n if (block.contains(target)) {\n const scrollElement = findScrollableParent(target, block)\n if (scrollElement) {\n iosActiveScrollElement = scrollElement\n break\n }\n }\n }\n }\n\n touchMoveHandler = (event: TouchEvent) => {\n const touch = event.touches[0]\n if (!touch || event.touches.length === 2 || isTouchMoveAllowed(event.target)) {\n return\n }\n\n if (!event.cancelable) {\n return\n }\n\n if (!iosActiveScrollElement || iosTouchStartY === null) {\n event.preventDefault()\n\n return\n }\n\n const currentY = touch.clientY\n const deltaY = currentY - Number(iosTouchStartY)\n const atTop = iosActiveScrollElement.scrollTop <= 0\n const atBottom =\n iosActiveScrollElement.scrollTop + iosActiveScrollElement.clientHeight >= iosActiveScrollElement.scrollHeight\n if ((atTop && deltaY > 0) || (atBottom && deltaY < 0)) {\n event.preventDefault()\n }\n }\n\n document.addEventListener('touchstart', touchStartHandler, TOUCH_EVENT_OPTIONS)\n document.addEventListener('touchmove', touchMoveHandler, TOUCH_EVENT_OPTIONS)\n}\n\nfunction removeIOSEvents() {\n if (touchStartHandler) {\n document.removeEventListener('touchstart', touchStartHandler, TOUCH_EVENT_OPTIONS)\n touchStartHandler = null\n }\n if (touchMoveHandler) {\n document.removeEventListener('touchmove', touchMoveHandler, TOUCH_EVENT_OPTIONS)\n touchMoveHandler = null\n }\n iosActiveScrollElement = null\n iosTouchStartY = null\n}\n\nfunction lockScrollIOS(scrollableContainer?: HTMLElement) {\n if (scrollableContainer) {\n iosScrollableBlocks.push(scrollableContainer)\n }\n const count = getScrollLockCount()\n if (count === 0) {\n applyScrollLockStyles()\n addIOSEvents()\n }\n setScrollLockCount(count + 1)\n}\n\nfunction unlockScrollIOS() {\n iosScrollableBlocks.pop()\n const count = getScrollLockCount()\n const nexCount = Math.max(0, count - 1)\n setScrollLockCount(nexCount)\n if (nexCount === 0) {\n resetScrollLockStyles()\n removeIOSEvents()\n }\n}\n\nfunction lockTargetScroll(element: HTMLElement) {\n const style = getComputedStyle(element)\n if (element.dataset[DATA_PREV_OVERFLOW] === undefined) {\n element.dataset[DATA_PREV_OVERFLOW] = style.overflow || ''\n }\n if (element.dataset[DATA_PREV_PADDING_RIGHT] === undefined) {\n element.dataset[DATA_PREV_PADDING_RIGHT] = style.paddingRight || ''\n }\n\n const borderLeft = parseFloat(style.borderLeftWidth) || 0\n const borderRight = parseFloat(style.borderRightWidth) || 0\n const borderWidth = borderLeft + borderRight\n const originalOverflow = style.overflow\n element.style.overflow = 'scroll'\n const scrollbarWidth = element.offsetWidth - element.clientWidth - borderWidth\n element.style.overflow = originalOverflow\n if (supportsScrollbarGutter) {\n element.style.scrollbarGutter = 'stable'\n } else if (scrollbarWidth > 0) {\n const currentPaddingRight = parseFloat(element.dataset[DATA_PREV_PADDING_RIGHT]) || 0\n element.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`\n }\n element.style.overflow = 'hidden'\n}\n\nfunction unlockTargetScroll(element: HTMLElement) {\n if (element.dataset[DATA_PREV_OVERFLOW] !== undefined) {\n element.style.overflow = element.dataset[DATA_PREV_OVERFLOW]\n }\n if (element.dataset[DATA_PREV_PADDING_RIGHT] !== undefined) {\n element.style.paddingRight = element.dataset[DATA_PREV_PADDING_RIGHT]\n }\n delete element.dataset[DATA_PREV_OVERFLOW]\n delete element.dataset[DATA_PREV_PADDING_RIGHT]\n if (supportsScrollbarGutter) {\n element.style.scrollbarGutter = ''\n }\n}\n\nfunction useScrollLock(params: ScrollLockParams) {\n const { locked, iosAllowedScrollContainer = document.documentElement, target } = params\n\n const isRootTarget = target === undefined || target === document.documentElement || target === document.body\n\n useEffect(() => {\n if (!target || isRootTarget) {\n return\n }\n\n const domTarget = getDomTargets(target)[0]\n if (!domTarget) {\n return\n }\n\n if (locked) {\n lockTargetScroll(domTarget)\n } else {\n if (domTarget.dataset[DATA_PREV_OVERFLOW] !== undefined) {\n unlockTargetScroll(domTarget)\n }\n }\n\n return () => {\n unlockTargetScroll(domTarget)\n }\n }, [target, locked, isRootTarget])\n\n useEffect(() => {\n if (!locked || !isRootTarget) {\n return\n }\n\n if (isIosDevice) {\n const domContainer = getDomTargets(iosAllowedScrollContainer)[0]\n lockScrollIOS(domContainer)\n\n return () => {\n unlockScrollIOS()\n }\n }\n\n lockScroll()\n\n return () => {\n unlockScroll()\n }\n }, [iosAllowedScrollContainer, isRootTarget, locked])\n}\n\nexport { useScrollLock }\n"],"names":["TOUCH_EVENT_OPTIONS","capture","passive","supportsScrollbarGutter","CSS","supports","isIosDevice","window","navigator","userAgent","test","includes","maxTouchPoints","iosScrollableBlocks","iosActiveScrollElement","iosTouchStartY","touchStartHandler","touchMoveHandler","getScrollLockCount","Number","document","documentElement","dataset","setScrollLockCount","count","toString","applyScrollLockStyles","root","style","overflow","scrollbarGutter","scrollbarWidth","innerWidth","clientWidth","paddingRight","currentPaddingRight","parseFloat","getComputedStyle","resetScrollLockStyles","isScrollable","element","overflowY","scrollHeight","clientHeight","findScrollableParent","startElement","parentElement","unlockTargetScroll","undefined","params","locked","iosAllowedScrollContainer","target","isRootTarget","body","useEffect","domTarget","getDomTargets","borderWidth","borderLeftWidth","borderRightWidth","originalOverflow","offsetWidth","lockTargetScroll","scrollableContainer","push","event","HTMLElement","touches","clientY","block","contains","scrollElement","touch","length","selection","ownerDocument","defaultView","getSelection","isCollapsed","containsNode","selectionStart","selectionEnd","activeElement","isTouchMoveAllowed","cancelable","preventDefault","deltaY","scrollTop","addEventListener","lockScrollIOS","pop","nexCount","Math","max","removeEventListener","unlockScrollIOS","lockScroll","currentCount","nextCount","unlockScroll"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useScrollLock.js","sources":["../../../src/hooks/useScrollLock.ts"],"sourcesContent":["import { useEffect } from 'react'\nimport type { DomTarget, Nullable } from 'shared/types'\nimport { getDomTargets } from 'shared/utils/getDomTargets'\n\ntype ScrollLockParams = {\n /**\n * Блокировать скролл.\n */\n locked: boolean\n /**\n * Контейнер, в котором разрешена прокрутка на iOS при заблокированном скролле страницы.\n */\n iosAllowedScrollContainer?: DomTarget\n /**\n * Элемент, на котором блокируется скролл.\n *\n * @default 'document.documentElement'\n */\n target?: DomTarget\n /**\n * Резервировать место под скроллбар (добавляет padding-right или scrollbar-gutter: stable).\n */\n reserveScrollbarGutter?: boolean\n}\n\nconst TOUCH_EVENT_OPTIONS = { capture: true, passive: false }\nconst DATA_SCROLL_LOCK_COUNT = 'scrollLockCount'\nconst DATA_PREV_OVERFLOW = 'prevOverflow'\nconst DATA_PREV_PADDING_RIGHT = 'prevPaddingRight'\n\nconst supportsScrollbarGutter =\n typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('scrollbar-gutter: stable')\nconst isIosDevice =\n typeof window !== 'undefined' &&\n window.navigator?.userAgent &&\n (/iP(ad|hone|od)/.test(window.navigator.userAgent) ||\n (window.navigator.userAgent.includes('Macintosh') && window.navigator.maxTouchPoints > 1))\n\nconst iosScrollableBlocks: HTMLElement[] = []\nlet iosActiveScrollElement: Nullable<HTMLElement> = null\nlet iosTouchStartY: Nullable<number> = null\nlet touchStartHandler: Nullable<(event: TouchEvent) => void> = null\nlet touchMoveHandler: Nullable<(event: TouchEvent) => void> = null\n\nfunction getScrollLockCount(): number {\n return Number(document.documentElement.dataset[DATA_SCROLL_LOCK_COUNT] || 0)\n}\n\nfunction setScrollLockCount(count: number) {\n document.documentElement.dataset[DATA_SCROLL_LOCK_COUNT] = count.toString()\n}\n\nfunction getScrollbarWidth(element: HTMLElement) {\n if (element === document.documentElement) {\n return window.innerWidth - document.documentElement.clientWidth\n }\n\n const style = getComputedStyle(element)\n const borderLeft = parseFloat(style.borderLeftWidth) || 0\n const borderRight = parseFloat(style.borderRightWidth) || 0\n const borderWidth = borderLeft + borderRight\n const originalOverflow = element.style.overflow\n element.style.overflow = 'scroll'\n const scrollbarWidth = element.offsetWidth - element.clientWidth - borderWidth\n element.style.overflow = originalOverflow\n\n return scrollbarWidth\n}\n\nfunction reserveScrollbar(element: HTMLElement) {\n if (supportsScrollbarGutter && !isIosDevice) {\n element.style.scrollbarGutter = 'stable'\n } else {\n const style = getComputedStyle(element)\n const scrollbarWidth = getScrollbarWidth(element)\n if (element.dataset[DATA_PREV_PADDING_RIGHT] === undefined) {\n element.dataset[DATA_PREV_PADDING_RIGHT] = element.style.paddingRight || ''\n }\n if (scrollbarWidth > 0) {\n const currentPaddingRight = parseFloat(style.paddingRight) || 0\n element.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`\n }\n }\n}\n\nfunction applyScrollLockStyles(reserveScrollbarGutter: boolean, locked = true, element = document.documentElement) {\n if (locked && element.dataset[DATA_PREV_OVERFLOW] === undefined) {\n element.dataset[DATA_PREV_OVERFLOW] = element.style.overflow || ''\n }\n if (reserveScrollbarGutter) {\n reserveScrollbar(element)\n }\n if (locked) {\n element.style.overflow = 'hidden'\n }\n}\n\nfunction resetScrollLockStyles(element = document.documentElement) {\n if (element.dataset[DATA_PREV_OVERFLOW] !== undefined) {\n element.style.overflow = element.dataset[DATA_PREV_OVERFLOW]\n }\n if (element.dataset[DATA_PREV_PADDING_RIGHT] !== undefined) {\n element.style.paddingRight = element.dataset[DATA_PREV_PADDING_RIGHT]\n }\n delete element.dataset[DATA_PREV_OVERFLOW]\n delete element.dataset[DATA_PREV_PADDING_RIGHT]\n if (supportsScrollbarGutter) {\n element.style.scrollbarGutter = ''\n }\n}\n\nfunction unlockScroll() {\n const currentCount = getScrollLockCount()\n if (currentCount <= 0) {\n return\n }\n\n const nextCount = currentCount - 1\n setScrollLockCount(nextCount)\n if (nextCount === 0) {\n resetScrollLockStyles()\n }\n}\n\nfunction lockScroll(reserveScrollbarGutter: boolean) {\n const count = getScrollLockCount()\n if (count === 0) {\n applyScrollLockStyles(reserveScrollbarGutter)\n }\n setScrollLockCount(count + 1)\n\n return unlockScroll\n}\n\nfunction isScrollable(element: HTMLElement): boolean {\n const style = getComputedStyle(element)\n const overflowY = style.overflowY === 'auto' || style.overflowY === 'scroll'\n\n return overflowY && element.scrollHeight > element.clientHeight\n}\n\nfunction findScrollableParent(startElement: HTMLElement, root: HTMLElement): Nullable<HTMLElement> {\n let element: Nullable<HTMLElement> = startElement\n\n while (element && element !== root.parentElement) {\n if (isScrollable(element)) {\n return element\n }\n\n element = element.parentElement\n }\n\n if (isScrollable(root)) {\n return root\n }\n\n return null\n}\n\nfunction isTouchMoveAllowed(target: Nullable<EventTarget>): boolean {\n if (!target || !(target instanceof HTMLElement)) {\n return false\n }\n\n const selection = target.ownerDocument?.defaultView?.getSelection()\n if (selection && !selection.isCollapsed && selection.containsNode(target, true)) {\n return true\n }\n\n if (\n 'selectionStart' in target &&\n 'selectionEnd' in target &&\n Number(target.selectionStart) < Number(target.selectionEnd) &&\n target.ownerDocument?.activeElement === target\n ) {\n return true\n }\n\n return false\n}\n\nfunction addIOSEvents() {\n if (touchMoveHandler || touchStartHandler) {\n return\n }\n\n touchStartHandler = (event: TouchEvent) => {\n const target = event.target\n if (!(target instanceof HTMLElement)) {\n iosActiveScrollElement = null\n iosTouchStartY = null\n\n return\n }\n\n iosTouchStartY = event.touches[0]?.clientY ?? null\n iosActiveScrollElement = null\n for (const block of iosScrollableBlocks) {\n if (block.contains(target)) {\n const scrollElement = findScrollableParent(target, block)\n if (scrollElement) {\n iosActiveScrollElement = scrollElement\n break\n }\n }\n }\n }\n\n touchMoveHandler = (event: TouchEvent) => {\n const touch = event.touches[0]\n if (!touch || event.touches.length === 2 || isTouchMoveAllowed(event.target)) {\n return\n }\n\n if (!event.cancelable) {\n return\n }\n\n if (!iosActiveScrollElement || iosTouchStartY === null) {\n event.preventDefault()\n\n return\n }\n\n const currentY = touch.clientY\n const deltaY = currentY - Number(iosTouchStartY)\n const atTop = iosActiveScrollElement.scrollTop <= 0\n const atBottom =\n iosActiveScrollElement.scrollTop + iosActiveScrollElement.clientHeight >= iosActiveScrollElement.scrollHeight\n if ((atTop && deltaY > 0) || (atBottom && deltaY < 0)) {\n event.preventDefault()\n }\n }\n\n document.addEventListener('touchstart', touchStartHandler, TOUCH_EVENT_OPTIONS)\n document.addEventListener('touchmove', touchMoveHandler, TOUCH_EVENT_OPTIONS)\n}\n\nfunction removeIOSEvents() {\n if (touchStartHandler) {\n document.removeEventListener('touchstart', touchStartHandler, TOUCH_EVENT_OPTIONS)\n touchStartHandler = null\n }\n if (touchMoveHandler) {\n document.removeEventListener('touchmove', touchMoveHandler, TOUCH_EVENT_OPTIONS)\n touchMoveHandler = null\n }\n iosActiveScrollElement = null\n iosTouchStartY = null\n}\n\nfunction unlockScrollIOS() {\n iosScrollableBlocks.pop()\n const count = getScrollLockCount()\n const nexCount = Math.max(0, count - 1)\n setScrollLockCount(nexCount)\n if (nexCount === 0) {\n resetScrollLockStyles()\n removeIOSEvents()\n }\n}\n\nfunction lockScrollIOS(reserveScrollbarGutter: boolean, scrollableContainer?: HTMLElement) {\n if (scrollableContainer) {\n iosScrollableBlocks.push(scrollableContainer)\n }\n const count = getScrollLockCount()\n if (count === 0) {\n applyScrollLockStyles(reserveScrollbarGutter)\n addIOSEvents()\n }\n setScrollLockCount(count + 1)\n\n return unlockScrollIOS\n}\n\nfunction lockTargetScroll(element: HTMLElement, locked: boolean, reserveScrollbarGutter: boolean) {\n applyScrollLockStyles(reserveScrollbarGutter, locked, element)\n\n return () => resetScrollLockStyles(element)\n}\n\nfunction useScrollLock(params: ScrollLockParams) {\n const {\n locked,\n iosAllowedScrollContainer = document.documentElement,\n target,\n reserveScrollbarGutter = false,\n } = params\n\n const isRootTarget = target === undefined || target === document.documentElement || target === document.body\n\n useEffect(() => {\n if (!target || isRootTarget) {\n return\n }\n\n const domTarget = getDomTargets(target)[0]\n if (!domTarget) {\n return\n }\n\n return lockTargetScroll(domTarget, locked, reserveScrollbarGutter)\n }, [target, locked, isRootTarget, reserveScrollbarGutter])\n\n useEffect(() => {\n if (!locked || !isRootTarget) {\n return\n }\n\n if (isIosDevice) {\n const domContainer = getDomTargets(iosAllowedScrollContainer)[0]\n\n return lockScrollIOS(reserveScrollbarGutter, domContainer)\n }\n\n return lockScroll(reserveScrollbarGutter)\n }, [iosAllowedScrollContainer, isRootTarget, locked, reserveScrollbarGutter])\n}\n\nexport { useScrollLock }\n"],"names":["TOUCH_EVENT_OPTIONS","capture","passive","supportsScrollbarGutter","CSS","supports","isIosDevice","window","navigator","userAgent","test","includes","maxTouchPoints","iosScrollableBlocks","iosActiveScrollElement","iosTouchStartY","touchStartHandler","touchMoveHandler","getScrollLockCount","Number","document","documentElement","dataset","setScrollLockCount","count","toString","applyScrollLockStyles","reserveScrollbarGutter","locked","element","undefined","style","overflow","scrollbarGutter","getComputedStyle","scrollbarWidth","innerWidth","clientWidth","borderWidth","parseFloat","borderLeftWidth","borderRightWidth","originalOverflow","offsetWidth","getScrollbarWidth","paddingRight","currentPaddingRight","reserveScrollbar","resetScrollLockStyles","unlockScroll","currentCount","nextCount","isScrollable","overflowY","scrollHeight","clientHeight","findScrollableParent","startElement","root","parentElement","unlockScrollIOS","pop","nexCount","Math","max","removeEventListener","params","iosAllowedScrollContainer","target","isRootTarget","body","useEffect","domTarget","getDomTargets","lockTargetScroll","domContainer","scrollableContainer","push","event","HTMLElement","touches","clientY","block","contains","scrollElement","touch","length","selection","ownerDocument","defaultView","getSelection","isCollapsed","containsNode","selectionStart","selectionEnd","activeElement","isTouchMoveAllowed","cancelable","preventDefault","deltaY","scrollTop","addEventListener","lockScrollIOS","lockScroll"],"mappings":"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"}
|
package/hooks/useScrollLock.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useEffect}from'react';import{getDomTargets}from'../shared/utils/getDomTargets.mjs';const TOUCH_EVENT_OPTIONS={capture:!0,passive:!1};const supportsScrollbarGutter=typeof CSS!='undefined'&&typeof CSS.supports=='function'&&CSS.supports('scrollbar-gutter: stable');const isIosDevice=typeof window!='undefined'&&window.navigator?.userAgent&&(/iP(ad|hone|od)/.test(window.navigator.userAgent)||window.navigator.userAgent.includes('Macintosh')&&window.navigator.maxTouchPoints>1);const iosScrollableBlocks=[];let iosActiveScrollElement=null;let iosTouchStartY=null;let touchStartHandler=null;let touchMoveHandler=null;function getScrollLockCount(){return Number(document.documentElement.dataset.scrollLockCount||0)}function setScrollLockCount(t){document.documentElement.dataset.scrollLockCount=t.toString()}function applyScrollLockStyles(
|
|
1
|
+
import{useEffect}from'react';import{getDomTargets}from'../shared/utils/getDomTargets.mjs';const TOUCH_EVENT_OPTIONS={capture:!0,passive:!1};const supportsScrollbarGutter=typeof CSS!='undefined'&&typeof CSS.supports=='function'&&CSS.supports('scrollbar-gutter: stable');const isIosDevice=typeof window!='undefined'&&window.navigator?.userAgent&&(/iP(ad|hone|od)/.test(window.navigator.userAgent)||window.navigator.userAgent.includes('Macintosh')&&window.navigator.maxTouchPoints>1);const iosScrollableBlocks=[];let iosActiveScrollElement=null;let iosTouchStartY=null;let touchStartHandler=null;let touchMoveHandler=null;function getScrollLockCount(){return Number(document.documentElement.dataset.scrollLockCount||0)}function setScrollLockCount(t){document.documentElement.dataset.scrollLockCount=t.toString()}function applyScrollLockStyles(t,e=!0,o=document.documentElement){e&&o.dataset.prevOverflow===void 0&&(o.dataset.prevOverflow=o.style.overflow||''),t&&(t=>{if(supportsScrollbarGutter&&!isIosDevice)t.style.scrollbarGutter='stable';else{const e=getComputedStyle(t);const o=(t=>{if(t===document.documentElement)return window.innerWidth-document.documentElement.clientWidth;const e=getComputedStyle(t);const o=(parseFloat(e.borderLeftWidth)||0)+(parseFloat(e.borderRightWidth)||0);const l=t.style.overflow;t.style.overflow='scroll';const n=t.offsetWidth-t.clientWidth-o;return t.style.overflow=l,n})(t);if(t.dataset.prevPaddingRight===void 0&&(t.dataset.prevPaddingRight=t.style.paddingRight||''),o>0){const l=parseFloat(e.paddingRight)||0;t.style.paddingRight=`${l+o}px`}}})(o),e&&(o.style.overflow='hidden')}function resetScrollLockStyles(t=document.documentElement){t.dataset.prevOverflow!==void 0&&(t.style.overflow=t.dataset.prevOverflow),t.dataset.prevPaddingRight!==void 0&&(t.style.paddingRight=t.dataset.prevPaddingRight),delete t.dataset.prevOverflow,delete t.dataset.prevPaddingRight,supportsScrollbarGutter&&(t.style.scrollbarGutter='')}function unlockScroll(){const t=getScrollLockCount();if(t<=0)return;const e=t-1;setScrollLockCount(e),e===0&&resetScrollLockStyles()}function isScrollable(t){const e=getComputedStyle(t);return(e.overflowY==='auto'||e.overflowY==='scroll')&&t.scrollHeight>t.clientHeight}function findScrollableParent(t,e){let o=t;for(;o&&o!==e.parentElement;){if(isScrollable(o))return o;o=o.parentElement}return isScrollable(e)?e:null}function unlockScrollIOS(){iosScrollableBlocks.pop();const t=getScrollLockCount();const e=Math.max(0,t-1);setScrollLockCount(e),e===0&&(resetScrollLockStyles(),touchStartHandler&&(document.removeEventListener('touchstart',touchStartHandler,TOUCH_EVENT_OPTIONS),touchStartHandler=null),touchMoveHandler&&(document.removeEventListener('touchmove',touchMoveHandler,TOUCH_EVENT_OPTIONS),touchMoveHandler=null),iosActiveScrollElement=null,iosTouchStartY=null)}function useScrollLock(t){const{locked:e,iosAllowedScrollContainer:o=document.documentElement,target:l,reserveScrollbarGutter:n=!1}=t;const r=l===void 0||l===document.documentElement||l===document.body;useEffect(()=>{if(!l||r)return;const t=getDomTargets(l)[0];return t?((t,e,o)=>(applyScrollLockStyles(o,e,t),()=>resetScrollLockStyles(t)))(t,e,n):void 0},[l,e,r,n]),useEffect(()=>{if(e&&r){if(isIosDevice){const t=getDomTargets(o)[0];return((t,e)=>{e&&iosScrollableBlocks.push(e);const o=getScrollLockCount();return o===0&&(applyScrollLockStyles(t),touchMoveHandler||touchStartHandler||(touchStartHandler=t=>{const e=t.target;if(!(e instanceof HTMLElement))return iosActiveScrollElement=null,void(iosTouchStartY=null);iosTouchStartY=t.touches[0]?.clientY??null,iosActiveScrollElement=null;for(const t of iosScrollableBlocks)if(t.contains(e)){const o=findScrollableParent(e,t);if(o){iosActiveScrollElement=o;break}}},touchMoveHandler=t=>{const e=t.touches[0];if(!e||t.touches.length===2||(t=>{if(!(t&&t instanceof HTMLElement))return!1;const e=t.ownerDocument?.defaultView?.getSelection();return!(!e||e.isCollapsed||!e.containsNode(t,!0))||'selectionStart'in t&&'selectionEnd'in t&&Number(t.selectionStart)<Number(t.selectionEnd)&&t.ownerDocument?.activeElement===t})(t.target))return;if(!t.cancelable)return;if(!iosActiveScrollElement||iosTouchStartY===null)return void t.preventDefault();const o=e.clientY-Number(iosTouchStartY);(iosActiveScrollElement.scrollTop<=0&&o>0||iosActiveScrollElement.scrollTop+iosActiveScrollElement.clientHeight>=iosActiveScrollElement.scrollHeight&&o<0)&&t.preventDefault()},document.addEventListener('touchstart',touchStartHandler,TOUCH_EVENT_OPTIONS),document.addEventListener('touchmove',touchMoveHandler,TOUCH_EVENT_OPTIONS))),setScrollLockCount(o+1),unlockScrollIOS})(n,t)}return(t=>{const e=getScrollLockCount();return e===0&&applyScrollLockStyles(t),setScrollLockCount(e+1),unlockScroll})(n)}},[o,r,e,n])}export{useScrollLock};
|
|
2
2
|
//# sourceMappingURL=useScrollLock.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollLock.mjs","sources":["../../../src/hooks/useScrollLock.ts"],"sourcesContent":["import { useEffect } from 'react'\nimport type { DomTarget, Nullable } from 'shared/types'\nimport { getDomTargets } from 'shared/utils/getDomTargets'\n\ntype ScrollLockParams = {\n /**\n * Блокировать скролл.\n */\n locked: boolean\n /**\n * Контейнер, в котором разрешена прокрутка на iOS при заблокированном скролле страницы.\n */\n iosAllowedScrollContainer?: DomTarget\n /**\n * Элемент, на котором блокируется скролл.\n *\n * @default 'document.documentElement'\n */\n target?: DomTarget\n}\n\nconst TOUCH_EVENT_OPTIONS = { capture: true, passive: false }\nconst DATA_SCROLL_LOCK_COUNT = 'scrollLockCount'\nconst DATA_PREV_OVERFLOW = 'prevOverflow'\nconst DATA_PREV_PADDING_RIGHT = 'prevPaddingRight'\n\nconst supportsScrollbarGutter =\n typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('scrollbar-gutter: stable')\nconst isIosDevice =\n typeof window !== 'undefined' &&\n window.navigator?.userAgent &&\n (/iP(ad|hone|od)/.test(window.navigator.userAgent) ||\n (window.navigator.userAgent.includes('Macintosh') && window.navigator.maxTouchPoints > 1))\n\nconst iosScrollableBlocks: HTMLElement[] = []\nlet iosActiveScrollElement: Nullable<HTMLElement> = null\nlet iosTouchStartY: Nullable<number> = null\nlet touchStartHandler: Nullable<(event: TouchEvent) => void> = null\nlet touchMoveHandler: Nullable<(event: TouchEvent) => void> = null\n\nfunction getScrollLockCount(): number {\n return Number(document.documentElement.dataset[DATA_SCROLL_LOCK_COUNT] || 0)\n}\n\nfunction setScrollLockCount(count: number) {\n document.documentElement.dataset[DATA_SCROLL_LOCK_COUNT] = count.toString()\n}\n\nfunction applyScrollLockStyles() {\n const root = document.documentElement\n root.dataset[DATA_PREV_OVERFLOW] = root.style.overflow || ''\n\n if (supportsScrollbarGutter && !isIosDevice) {\n root.style.scrollbarGutter = 'stable'\n } else {\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth\n root.dataset[DATA_PREV_PADDING_RIGHT] = root.style.paddingRight || ''\n if (scrollbarWidth > 0) {\n const currentPaddingRight = parseFloat(getComputedStyle(root).paddingRight) || 0\n root.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`\n }\n }\n root.style.overflow = 'hidden'\n}\n\nfunction resetScrollLockStyles() {\n const root = document.documentElement\n root.style.overflow = root.dataset[DATA_PREV_OVERFLOW] || ''\n root.style.paddingRight = root.dataset[DATA_PREV_PADDING_RIGHT] || ''\n delete root.dataset[DATA_PREV_OVERFLOW]\n delete root.dataset[DATA_PREV_PADDING_RIGHT]\n}\n\nfunction lockScroll() {\n const count = getScrollLockCount()\n if (count === 0) {\n applyScrollLockStyles()\n }\n setScrollLockCount(count + 1)\n}\n\nfunction unlockScroll() {\n const currentCount = getScrollLockCount()\n if (currentCount <= 0) {\n return\n }\n\n const nextCount = currentCount - 1\n setScrollLockCount(nextCount)\n if (nextCount === 0) {\n resetScrollLockStyles()\n }\n}\n\nfunction isScrollable(element: HTMLElement): boolean {\n const style = getComputedStyle(element)\n const overflowY = style.overflowY === 'auto' || style.overflowY === 'scroll'\n\n return overflowY && element.scrollHeight > element.clientHeight\n}\n\nfunction findScrollableParent(startElement: HTMLElement, root: HTMLElement): Nullable<HTMLElement> {\n let element: Nullable<HTMLElement> = startElement\n\n while (element && element !== root.parentElement) {\n if (isScrollable(element)) {\n return element\n }\n\n element = element.parentElement\n }\n\n if (isScrollable(root)) {\n return root\n }\n\n return null\n}\n\nfunction isTouchMoveAllowed(target: Nullable<EventTarget>): boolean {\n if (!target || !(target instanceof HTMLElement)) {\n return false\n }\n\n const selection = target.ownerDocument?.defaultView?.getSelection()\n if (selection && !selection.isCollapsed && selection.containsNode(target, true)) {\n return true\n }\n\n if (\n 'selectionStart' in target &&\n 'selectionEnd' in target &&\n Number(target.selectionStart) < Number(target.selectionEnd) &&\n target.ownerDocument?.activeElement === target\n ) {\n return true\n }\n\n return false\n}\n\nfunction addIOSEvents() {\n if (touchMoveHandler || touchStartHandler) {\n return\n }\n\n touchStartHandler = (event: TouchEvent) => {\n const target = event.target\n if (!(target instanceof HTMLElement)) {\n iosActiveScrollElement = null\n iosTouchStartY = null\n\n return\n }\n\n iosTouchStartY = event.touches[0]?.clientY ?? null\n iosActiveScrollElement = null\n for (const block of iosScrollableBlocks) {\n if (block.contains(target)) {\n const scrollElement = findScrollableParent(target, block)\n if (scrollElement) {\n iosActiveScrollElement = scrollElement\n break\n }\n }\n }\n }\n\n touchMoveHandler = (event: TouchEvent) => {\n const touch = event.touches[0]\n if (!touch || event.touches.length === 2 || isTouchMoveAllowed(event.target)) {\n return\n }\n\n if (!event.cancelable) {\n return\n }\n\n if (!iosActiveScrollElement || iosTouchStartY === null) {\n event.preventDefault()\n\n return\n }\n\n const currentY = touch.clientY\n const deltaY = currentY - Number(iosTouchStartY)\n const atTop = iosActiveScrollElement.scrollTop <= 0\n const atBottom =\n iosActiveScrollElement.scrollTop + iosActiveScrollElement.clientHeight >= iosActiveScrollElement.scrollHeight\n if ((atTop && deltaY > 0) || (atBottom && deltaY < 0)) {\n event.preventDefault()\n }\n }\n\n document.addEventListener('touchstart', touchStartHandler, TOUCH_EVENT_OPTIONS)\n document.addEventListener('touchmove', touchMoveHandler, TOUCH_EVENT_OPTIONS)\n}\n\nfunction removeIOSEvents() {\n if (touchStartHandler) {\n document.removeEventListener('touchstart', touchStartHandler, TOUCH_EVENT_OPTIONS)\n touchStartHandler = null\n }\n if (touchMoveHandler) {\n document.removeEventListener('touchmove', touchMoveHandler, TOUCH_EVENT_OPTIONS)\n touchMoveHandler = null\n }\n iosActiveScrollElement = null\n iosTouchStartY = null\n}\n\nfunction lockScrollIOS(scrollableContainer?: HTMLElement) {\n if (scrollableContainer) {\n iosScrollableBlocks.push(scrollableContainer)\n }\n const count = getScrollLockCount()\n if (count === 0) {\n applyScrollLockStyles()\n addIOSEvents()\n }\n setScrollLockCount(count + 1)\n}\n\nfunction unlockScrollIOS() {\n iosScrollableBlocks.pop()\n const count = getScrollLockCount()\n const nexCount = Math.max(0, count - 1)\n setScrollLockCount(nexCount)\n if (nexCount === 0) {\n resetScrollLockStyles()\n removeIOSEvents()\n }\n}\n\nfunction lockTargetScroll(element: HTMLElement) {\n const style = getComputedStyle(element)\n if (element.dataset[DATA_PREV_OVERFLOW] === undefined) {\n element.dataset[DATA_PREV_OVERFLOW] = style.overflow || ''\n }\n if (element.dataset[DATA_PREV_PADDING_RIGHT] === undefined) {\n element.dataset[DATA_PREV_PADDING_RIGHT] = style.paddingRight || ''\n }\n\n const borderLeft = parseFloat(style.borderLeftWidth) || 0\n const borderRight = parseFloat(style.borderRightWidth) || 0\n const borderWidth = borderLeft + borderRight\n const originalOverflow = style.overflow\n element.style.overflow = 'scroll'\n const scrollbarWidth = element.offsetWidth - element.clientWidth - borderWidth\n element.style.overflow = originalOverflow\n if (supportsScrollbarGutter) {\n element.style.scrollbarGutter = 'stable'\n } else if (scrollbarWidth > 0) {\n const currentPaddingRight = parseFloat(element.dataset[DATA_PREV_PADDING_RIGHT]) || 0\n element.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`\n }\n element.style.overflow = 'hidden'\n}\n\nfunction unlockTargetScroll(element: HTMLElement) {\n if (element.dataset[DATA_PREV_OVERFLOW] !== undefined) {\n element.style.overflow = element.dataset[DATA_PREV_OVERFLOW]\n }\n if (element.dataset[DATA_PREV_PADDING_RIGHT] !== undefined) {\n element.style.paddingRight = element.dataset[DATA_PREV_PADDING_RIGHT]\n }\n delete element.dataset[DATA_PREV_OVERFLOW]\n delete element.dataset[DATA_PREV_PADDING_RIGHT]\n if (supportsScrollbarGutter) {\n element.style.scrollbarGutter = ''\n }\n}\n\nfunction useScrollLock(params: ScrollLockParams) {\n const { locked, iosAllowedScrollContainer = document.documentElement, target } = params\n\n const isRootTarget = target === undefined || target === document.documentElement || target === document.body\n\n useEffect(() => {\n if (!target || isRootTarget) {\n return\n }\n\n const domTarget = getDomTargets(target)[0]\n if (!domTarget) {\n return\n }\n\n if (locked) {\n lockTargetScroll(domTarget)\n } else {\n if (domTarget.dataset[DATA_PREV_OVERFLOW] !== undefined) {\n unlockTargetScroll(domTarget)\n }\n }\n\n return () => {\n unlockTargetScroll(domTarget)\n }\n }, [target, locked, isRootTarget])\n\n useEffect(() => {\n if (!locked || !isRootTarget) {\n return\n }\n\n if (isIosDevice) {\n const domContainer = getDomTargets(iosAllowedScrollContainer)[0]\n lockScrollIOS(domContainer)\n\n return () => {\n unlockScrollIOS()\n }\n }\n\n lockScroll()\n\n return () => {\n unlockScroll()\n }\n }, [iosAllowedScrollContainer, isRootTarget, locked])\n}\n\nexport { useScrollLock }\n"],"names":["TOUCH_EVENT_OPTIONS","capture","passive","supportsScrollbarGutter","CSS","supports","isIosDevice","window","navigator","userAgent","test","includes","maxTouchPoints","iosScrollableBlocks","iosActiveScrollElement","iosTouchStartY","touchStartHandler","touchMoveHandler","getScrollLockCount","Number","document","documentElement","dataset","setScrollLockCount","count","toString","applyScrollLockStyles","root","style","overflow","scrollbarGutter","scrollbarWidth","innerWidth","clientWidth","paddingRight","currentPaddingRight","parseFloat","getComputedStyle","resetScrollLockStyles","isScrollable","element","overflowY","scrollHeight","clientHeight","findScrollableParent","startElement","parentElement","unlockTargetScroll","undefined","useScrollLock","params","locked","iosAllowedScrollContainer","target","isRootTarget","body","useEffect","domTarget","getDomTargets","borderWidth","borderLeftWidth","borderRightWidth","originalOverflow","offsetWidth","lockTargetScroll","scrollableContainer","push","event","HTMLElement","touches","clientY","block","contains","scrollElement","touch","length","selection","ownerDocument","defaultView","getSelection","isCollapsed","containsNode","selectionStart","selectionEnd","activeElement","isTouchMoveAllowed","cancelable","preventDefault","deltaY","scrollTop","addEventListener","lockScrollIOS","pop","nexCount","Math","max","removeEventListener","unlockScrollIOS","lockScroll","currentCount","nextCount","unlockScroll"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useScrollLock.mjs","sources":["../../../src/hooks/useScrollLock.ts"],"sourcesContent":["import { useEffect } from 'react'\nimport type { DomTarget, Nullable } from 'shared/types'\nimport { getDomTargets } from 'shared/utils/getDomTargets'\n\ntype ScrollLockParams = {\n /**\n * Блокировать скролл.\n */\n locked: boolean\n /**\n * Контейнер, в котором разрешена прокрутка на iOS при заблокированном скролле страницы.\n */\n iosAllowedScrollContainer?: DomTarget\n /**\n * Элемент, на котором блокируется скролл.\n *\n * @default 'document.documentElement'\n */\n target?: DomTarget\n /**\n * Резервировать место под скроллбар (добавляет padding-right или scrollbar-gutter: stable).\n */\n reserveScrollbarGutter?: boolean\n}\n\nconst TOUCH_EVENT_OPTIONS = { capture: true, passive: false }\nconst DATA_SCROLL_LOCK_COUNT = 'scrollLockCount'\nconst DATA_PREV_OVERFLOW = 'prevOverflow'\nconst DATA_PREV_PADDING_RIGHT = 'prevPaddingRight'\n\nconst supportsScrollbarGutter =\n typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('scrollbar-gutter: stable')\nconst isIosDevice =\n typeof window !== 'undefined' &&\n window.navigator?.userAgent &&\n (/iP(ad|hone|od)/.test(window.navigator.userAgent) ||\n (window.navigator.userAgent.includes('Macintosh') && window.navigator.maxTouchPoints > 1))\n\nconst iosScrollableBlocks: HTMLElement[] = []\nlet iosActiveScrollElement: Nullable<HTMLElement> = null\nlet iosTouchStartY: Nullable<number> = null\nlet touchStartHandler: Nullable<(event: TouchEvent) => void> = null\nlet touchMoveHandler: Nullable<(event: TouchEvent) => void> = null\n\nfunction getScrollLockCount(): number {\n return Number(document.documentElement.dataset[DATA_SCROLL_LOCK_COUNT] || 0)\n}\n\nfunction setScrollLockCount(count: number) {\n document.documentElement.dataset[DATA_SCROLL_LOCK_COUNT] = count.toString()\n}\n\nfunction getScrollbarWidth(element: HTMLElement) {\n if (element === document.documentElement) {\n return window.innerWidth - document.documentElement.clientWidth\n }\n\n const style = getComputedStyle(element)\n const borderLeft = parseFloat(style.borderLeftWidth) || 0\n const borderRight = parseFloat(style.borderRightWidth) || 0\n const borderWidth = borderLeft + borderRight\n const originalOverflow = element.style.overflow\n element.style.overflow = 'scroll'\n const scrollbarWidth = element.offsetWidth - element.clientWidth - borderWidth\n element.style.overflow = originalOverflow\n\n return scrollbarWidth\n}\n\nfunction reserveScrollbar(element: HTMLElement) {\n if (supportsScrollbarGutter && !isIosDevice) {\n element.style.scrollbarGutter = 'stable'\n } else {\n const style = getComputedStyle(element)\n const scrollbarWidth = getScrollbarWidth(element)\n if (element.dataset[DATA_PREV_PADDING_RIGHT] === undefined) {\n element.dataset[DATA_PREV_PADDING_RIGHT] = element.style.paddingRight || ''\n }\n if (scrollbarWidth > 0) {\n const currentPaddingRight = parseFloat(style.paddingRight) || 0\n element.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`\n }\n }\n}\n\nfunction applyScrollLockStyles(reserveScrollbarGutter: boolean, locked = true, element = document.documentElement) {\n if (locked && element.dataset[DATA_PREV_OVERFLOW] === undefined) {\n element.dataset[DATA_PREV_OVERFLOW] = element.style.overflow || ''\n }\n if (reserveScrollbarGutter) {\n reserveScrollbar(element)\n }\n if (locked) {\n element.style.overflow = 'hidden'\n }\n}\n\nfunction resetScrollLockStyles(element = document.documentElement) {\n if (element.dataset[DATA_PREV_OVERFLOW] !== undefined) {\n element.style.overflow = element.dataset[DATA_PREV_OVERFLOW]\n }\n if (element.dataset[DATA_PREV_PADDING_RIGHT] !== undefined) {\n element.style.paddingRight = element.dataset[DATA_PREV_PADDING_RIGHT]\n }\n delete element.dataset[DATA_PREV_OVERFLOW]\n delete element.dataset[DATA_PREV_PADDING_RIGHT]\n if (supportsScrollbarGutter) {\n element.style.scrollbarGutter = ''\n }\n}\n\nfunction unlockScroll() {\n const currentCount = getScrollLockCount()\n if (currentCount <= 0) {\n return\n }\n\n const nextCount = currentCount - 1\n setScrollLockCount(nextCount)\n if (nextCount === 0) {\n resetScrollLockStyles()\n }\n}\n\nfunction lockScroll(reserveScrollbarGutter: boolean) {\n const count = getScrollLockCount()\n if (count === 0) {\n applyScrollLockStyles(reserveScrollbarGutter)\n }\n setScrollLockCount(count + 1)\n\n return unlockScroll\n}\n\nfunction isScrollable(element: HTMLElement): boolean {\n const style = getComputedStyle(element)\n const overflowY = style.overflowY === 'auto' || style.overflowY === 'scroll'\n\n return overflowY && element.scrollHeight > element.clientHeight\n}\n\nfunction findScrollableParent(startElement: HTMLElement, root: HTMLElement): Nullable<HTMLElement> {\n let element: Nullable<HTMLElement> = startElement\n\n while (element && element !== root.parentElement) {\n if (isScrollable(element)) {\n return element\n }\n\n element = element.parentElement\n }\n\n if (isScrollable(root)) {\n return root\n }\n\n return null\n}\n\nfunction isTouchMoveAllowed(target: Nullable<EventTarget>): boolean {\n if (!target || !(target instanceof HTMLElement)) {\n return false\n }\n\n const selection = target.ownerDocument?.defaultView?.getSelection()\n if (selection && !selection.isCollapsed && selection.containsNode(target, true)) {\n return true\n }\n\n if (\n 'selectionStart' in target &&\n 'selectionEnd' in target &&\n Number(target.selectionStart) < Number(target.selectionEnd) &&\n target.ownerDocument?.activeElement === target\n ) {\n return true\n }\n\n return false\n}\n\nfunction addIOSEvents() {\n if (touchMoveHandler || touchStartHandler) {\n return\n }\n\n touchStartHandler = (event: TouchEvent) => {\n const target = event.target\n if (!(target instanceof HTMLElement)) {\n iosActiveScrollElement = null\n iosTouchStartY = null\n\n return\n }\n\n iosTouchStartY = event.touches[0]?.clientY ?? null\n iosActiveScrollElement = null\n for (const block of iosScrollableBlocks) {\n if (block.contains(target)) {\n const scrollElement = findScrollableParent(target, block)\n if (scrollElement) {\n iosActiveScrollElement = scrollElement\n break\n }\n }\n }\n }\n\n touchMoveHandler = (event: TouchEvent) => {\n const touch = event.touches[0]\n if (!touch || event.touches.length === 2 || isTouchMoveAllowed(event.target)) {\n return\n }\n\n if (!event.cancelable) {\n return\n }\n\n if (!iosActiveScrollElement || iosTouchStartY === null) {\n event.preventDefault()\n\n return\n }\n\n const currentY = touch.clientY\n const deltaY = currentY - Number(iosTouchStartY)\n const atTop = iosActiveScrollElement.scrollTop <= 0\n const atBottom =\n iosActiveScrollElement.scrollTop + iosActiveScrollElement.clientHeight >= iosActiveScrollElement.scrollHeight\n if ((atTop && deltaY > 0) || (atBottom && deltaY < 0)) {\n event.preventDefault()\n }\n }\n\n document.addEventListener('touchstart', touchStartHandler, TOUCH_EVENT_OPTIONS)\n document.addEventListener('touchmove', touchMoveHandler, TOUCH_EVENT_OPTIONS)\n}\n\nfunction removeIOSEvents() {\n if (touchStartHandler) {\n document.removeEventListener('touchstart', touchStartHandler, TOUCH_EVENT_OPTIONS)\n touchStartHandler = null\n }\n if (touchMoveHandler) {\n document.removeEventListener('touchmove', touchMoveHandler, TOUCH_EVENT_OPTIONS)\n touchMoveHandler = null\n }\n iosActiveScrollElement = null\n iosTouchStartY = null\n}\n\nfunction unlockScrollIOS() {\n iosScrollableBlocks.pop()\n const count = getScrollLockCount()\n const nexCount = Math.max(0, count - 1)\n setScrollLockCount(nexCount)\n if (nexCount === 0) {\n resetScrollLockStyles()\n removeIOSEvents()\n }\n}\n\nfunction lockScrollIOS(reserveScrollbarGutter: boolean, scrollableContainer?: HTMLElement) {\n if (scrollableContainer) {\n iosScrollableBlocks.push(scrollableContainer)\n }\n const count = getScrollLockCount()\n if (count === 0) {\n applyScrollLockStyles(reserveScrollbarGutter)\n addIOSEvents()\n }\n setScrollLockCount(count + 1)\n\n return unlockScrollIOS\n}\n\nfunction lockTargetScroll(element: HTMLElement, locked: boolean, reserveScrollbarGutter: boolean) {\n applyScrollLockStyles(reserveScrollbarGutter, locked, element)\n\n return () => resetScrollLockStyles(element)\n}\n\nfunction useScrollLock(params: ScrollLockParams) {\n const {\n locked,\n iosAllowedScrollContainer = document.documentElement,\n target,\n reserveScrollbarGutter = false,\n } = params\n\n const isRootTarget = target === undefined || target === document.documentElement || target === document.body\n\n useEffect(() => {\n if (!target || isRootTarget) {\n return\n }\n\n const domTarget = getDomTargets(target)[0]\n if (!domTarget) {\n return\n }\n\n return lockTargetScroll(domTarget, locked, reserveScrollbarGutter)\n }, [target, locked, isRootTarget, reserveScrollbarGutter])\n\n useEffect(() => {\n if (!locked || !isRootTarget) {\n return\n }\n\n if (isIosDevice) {\n const domContainer = getDomTargets(iosAllowedScrollContainer)[0]\n\n return lockScrollIOS(reserveScrollbarGutter, domContainer)\n }\n\n return lockScroll(reserveScrollbarGutter)\n }, [iosAllowedScrollContainer, isRootTarget, locked, reserveScrollbarGutter])\n}\n\nexport { useScrollLock }\n"],"names":["TOUCH_EVENT_OPTIONS","capture","passive","supportsScrollbarGutter","CSS","supports","isIosDevice","window","navigator","userAgent","test","includes","maxTouchPoints","iosScrollableBlocks","iosActiveScrollElement","iosTouchStartY","touchStartHandler","touchMoveHandler","getScrollLockCount","Number","document","documentElement","dataset","setScrollLockCount","count","toString","applyScrollLockStyles","reserveScrollbarGutter","locked","element","undefined","style","overflow","scrollbarGutter","getComputedStyle","scrollbarWidth","innerWidth","clientWidth","borderWidth","parseFloat","borderLeftWidth","borderRightWidth","originalOverflow","offsetWidth","getScrollbarWidth","paddingRight","currentPaddingRight","reserveScrollbar","resetScrollLockStyles","unlockScroll","currentCount","nextCount","isScrollable","overflowY","scrollHeight","clientHeight","findScrollableParent","startElement","root","parentElement","unlockScrollIOS","pop","nexCount","Math","max","removeEventListener","useScrollLock","params","iosAllowedScrollContainer","target","isRootTarget","body","useEffect","domTarget","getDomTargets","lockTargetScroll","domContainer","scrollableContainer","push","event","HTMLElement","touches","clientY","block","contains","scrollElement","touch","length","selection","ownerDocument","defaultView","getSelection","isCollapsed","containsNode","selectionStart","selectionEnd","activeElement","isTouchMoveAllowed","cancelable","preventDefault","deltaY","scrollTop","addEventListener","lockScrollIOS","lockScroll"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foxford/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.127.0",
|
|
4
4
|
"description": "UI components and utilities",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/foxford/ui/issues"
|
|
@@ -60,6 +60,6 @@
|
|
|
60
60
|
"shared",
|
|
61
61
|
"theme"
|
|
62
62
|
],
|
|
63
|
-
"sha": "
|
|
63
|
+
"sha": "435549b",
|
|
64
64
|
"scripts": {}
|
|
65
65
|
}
|