@mrshmllw/smores-react 16.6.0 → 16.7.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.
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useRef } from 'react';
2
+ import { useEffect, useId, useRef, useState } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import styled, { css, useTheme } from 'styled-components';
5
5
  import { Box } from '../Box';
@@ -14,16 +14,74 @@ const getDefaultTitleProps = (title) => ({
14
14
  typo: 'heading-small',
15
15
  align: 'left',
16
16
  });
17
+ const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
17
18
  export const Modal = ({ title = '', children, rightPanel, showModal = false, handleClick, drawer = true, cross = true, width, containerClass, portalContainer = document.body, closeOnOverlayClick = true, stickyHeader = false, footer, stickyFooter = false, }) => {
18
- const modalRef = useRef(null);
19
+ const [modalNode, setModalNode] = useState(null);
20
+ const containerRef = useRef(null);
21
+ const titleId = useId();
19
22
  const theme = useTheme();
20
- // oxlint-disable-next-line react/react-compiler
21
- useBodyScrollLock({ node: modalRef.current, showModal });
23
+ useBodyScrollLock({ node: modalNode, showModal });
24
+ // Record the opener while rendering the open state, before any child can take focus.
25
+ const [wasOpen, setWasOpen] = useState(false);
26
+ const [opener, setOpener] = useState(null);
27
+ if (showModal !== wasOpen) {
28
+ setWasOpen(showModal);
29
+ if (showModal)
30
+ setOpener(document.activeElement);
31
+ }
32
+ const latestRef = useRef({ handleClick, closeOnOverlayClick });
33
+ useEffect(() => {
34
+ latestRef.current = { handleClick, closeOnOverlayClick };
35
+ });
36
+ useEffect(() => {
37
+ const container = containerRef.current;
38
+ if (!showModal || !container)
39
+ return;
40
+ // A child with autoFocus may already have focus, so leave it there.
41
+ if (!container.contains(document.activeElement))
42
+ container.focus();
43
+ const handleKeyDown = (event) => {
44
+ const active = document.activeElement;
45
+ // Only the modal that has focus responds, so stacked modals close one at a time.
46
+ if (!container.contains(active))
47
+ return;
48
+ if (event.key === 'Escape') {
49
+ if (event.defaultPrevented || event.isComposing)
50
+ return;
51
+ if (latestRef.current.closeOnOverlayClick) {
52
+ latestRef.current.handleClick();
53
+ }
54
+ return;
55
+ }
56
+ if (event.key !== 'Tab')
57
+ return;
58
+ const focusable = container.querySelectorAll(FOCUSABLE_SELECTOR);
59
+ const first = focusable[0];
60
+ const last = focusable[focusable.length - 1];
61
+ if (!first) {
62
+ event.preventDefault();
63
+ }
64
+ else if (event.shiftKey && (active === first || active === container)) {
65
+ event.preventDefault();
66
+ last.focus();
67
+ }
68
+ else if (!event.shiftKey && active === last) {
69
+ event.preventDefault();
70
+ first.focus();
71
+ }
72
+ };
73
+ document.addEventListener('keydown', handleKeyDown);
74
+ return () => {
75
+ document.removeEventListener('keydown', handleKeyDown);
76
+ opener?.focus();
77
+ };
78
+ }, [showModal, opener]);
22
79
  const isTitleString = typeof title === 'string';
23
80
  const titleProps = isTitleString ? getDefaultTitleProps(title) : title;
81
+ const labelId = titleProps.id ?? titleId;
24
82
  if (!showModal)
25
83
  return null;
26
- return createPortal(_jsxs(Wrapper, { ref: modalRef, children: [_jsx(Overlay, { onClick: () => closeOnOverlayClick && handleClick(), $closeOnOverlayClick: closeOnOverlayClick }), _jsx(Container, { $drawer: drawer, $width: width || '460px', className: containerClass, children: _jsxs(ScrollArea, { children: [_jsxs(Header, { flex: true, alignItems: "flex-start", justifyContent: "space-between", $sticky: stickyHeader, children: [_jsx(TitleElements, { flex: true, direction: "column", children: _jsx(Text, { ...titleProps }) }), _jsxs(Box, { flex: true, alignItems: "center", gap: 'space.100', children: [rightPanel, cross && (_jsx(IconContainer, { as: "button", onClick: handleClick, role: "button", title: "Close modal", $size: 32, style: {
84
+ return createPortal(_jsxs(Wrapper, { ref: setModalNode, children: [_jsx(Overlay, { onClick: () => closeOnOverlayClick && handleClick(), $closeOnOverlayClick: closeOnOverlayClick }), _jsx(Container, { ref: containerRef, tabIndex: -1, role: "dialog", "aria-modal": "true", "aria-labelledby": title ? labelId : undefined, $drawer: drawer, $width: width || '460px', className: containerClass, children: _jsxs(ScrollArea, { children: [_jsxs(Header, { flex: true, alignItems: "flex-start", justifyContent: "space-between", $sticky: stickyHeader, children: [_jsx(TitleElements, { flex: true, direction: "column", children: _jsx(Text, { ...titleProps, id: labelId }) }), _jsxs(Box, { flex: true, alignItems: "center", gap: 'space.100', children: [rightPanel, cross && (_jsx(IconContainer, { as: "button", onClick: handleClick, role: "button", title: "Close modal", $size: 32, style: {
27
85
  background: theme.color.illustration.neutral[300],
28
86
  borderRadius: '100%',
29
87
  padding: '6px',
@@ -63,6 +121,7 @@ const Container = styled.div(({ $drawer, $width }) => css `
63
121
  position: fixed;
64
122
  max-height: calc(100vh - 64px);
65
123
  overflow: hidden;
124
+ outline: none;
66
125
  transition: all 0.3s ease-in-out;
67
126
 
68
127
  ${$drawer === true &&
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../src/Modal/Modal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { FC, ReactNode, useRef } from 'react'\nimport { createPortal } from 'react-dom'\nimport styled, { css, useTheme } from 'styled-components'\n\nimport { Box } from '../Box'\nimport { Text, type TextProps } from '../Text'\nimport { useBodyScrollLock } from '../hooks/useBodyScrollLock'\nimport { IconContainer } from '../sharedStyles/shared.styles'\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\nimport { faXmark } from '@awesome.me/kit-46ca99185c/icons/classic/regular'\n\ninterface IModalContainer {\n // drawer state\n $drawer: boolean\n // modal width\n $width: string\n}\n\nexport type ModalProps = {\n /**\n * Title of the modal\n * @default \"\" (empty string)\n *\n * @example\n * ```tsx\n * <Modal title=\"MultiCar Account\" />\n * ```\n *\n * @example\n * ```tsx\n * <Modal title={{ typo: 'hero', children: 'MultiCar Account' }} />\n * ```\n */\n title?: string | TitleProps\n icon?: string\n children?: ReactNode\n rightPanel?: ReactNode\n showModal?: boolean\n handleClick: () => void\n drawer?: boolean\n cross?: boolean\n width?: string\n containerClass?: string\n portalContainer?: Element | DocumentFragment\n closeOnOverlayClick?: boolean\n /**\n * Keeps the title and close button visible while the content scrolls.\n * @default false\n */\n stickyHeader?: boolean\n /**\n * Rendered below the content, e.g. one or two buttons, which share the\n * width equally. Put the primary action last.\n */\n footer?: ReactNode\n /**\n * Keeps `footer` visible while the content scrolls.\n * @default false\n */\n stickyFooter?: boolean\n}\n\nexport type TitleProps = TextProps\n\nconst getDefaultTitleProps = (title: string): TitleProps => ({\n children: title,\n tag: 'h2',\n typo: 'heading-small',\n align: 'left',\n})\n\nexport const Modal: FC<ModalProps> = ({\n title = '',\n children,\n rightPanel,\n showModal = false,\n handleClick,\n drawer = true,\n cross = true,\n width,\n containerClass,\n portalContainer = document.body,\n closeOnOverlayClick = true,\n stickyHeader = false,\n footer,\n stickyFooter = false,\n}) => {\n const modalRef = useRef<HTMLDivElement>(null)\n const theme = useTheme()\n\n // oxlint-disable-next-line react/react-compiler\n useBodyScrollLock({ node: modalRef.current, showModal })\n\n const isTitleString = typeof title === 'string'\n const titleProps = isTitleString ? getDefaultTitleProps(title) : title\n\n if (!showModal) return null\n\n return createPortal(\n <Wrapper ref={modalRef}>\n <Overlay\n onClick={() => closeOnOverlayClick && handleClick()}\n $closeOnOverlayClick={closeOnOverlayClick}\n />\n <Container\n $drawer={drawer}\n $width={width || '460px'}\n className={containerClass}\n >\n <ScrollArea>\n <Header\n flex\n alignItems=\"flex-start\"\n justifyContent=\"space-between\"\n $sticky={stickyHeader}\n >\n <TitleElements flex direction=\"column\">\n <Text {...titleProps} />\n </TitleElements>\n <Box flex alignItems=\"center\" gap={'space.100'}>\n {rightPanel}\n {cross && (\n <IconContainer\n as=\"button\"\n onClick={handleClick}\n role=\"button\"\n title=\"Close modal\"\n $size={32}\n style={{\n background: theme.color.illustration.neutral[300],\n borderRadius: '100%',\n padding: '6px',\n border: 'none',\n cursor: 'pointer',\n }}\n >\n <FontAwesomeIcon\n icon={faXmark}\n color={theme.color.icon.base}\n />\n </IconContainer>\n )}\n </Box>\n </Header>\n <ContentArea flex direction=\"column\" $hasFooter={!!footer}>\n {children}\n </ContentArea>\n {footer ? <Footer $sticky={stickyFooter}>{footer}</Footer> : null}\n </ScrollArea>\n </Container>\n </Wrapper>,\n portalContainer,\n )\n}\n\nconst Wrapper = styled(Box)`\n display: flex;\n position: absolute;\n z-index: 999;\n top: 0;\n left: 0;\n height: 100vh;\n width: 100%;\n justify-content: center;\n align-items: center;\n`\n\nconst Overlay = styled.div<{ $closeOnOverlayClick: boolean }>`\n position: fixed;\n background: ${({ theme }) => theme.color.surface.base[900]};\n cursor: ${(props) => (props.$closeOnOverlayClick ? 'pointer' : 'default')};\n opacity: 0.4;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n`\n\nconst Container = styled.div<IModalContainer>(\n ({ $drawer, $width }) => css`\n display: flex;\n flex-direction: column;\n background: ${({ theme }) => theme.color.background[100]};\n box-sizing: border-box;\n border-radius: 16px;\n width: 100%;\n max-width: ${$width};\n position: fixed;\n max-height: calc(100vh - 64px);\n overflow: hidden;\n transition: all 0.3s ease-in-out;\n\n ${\n $drawer === true &&\n css`\n @media (max-width: 768px) {\n max-width: none;\n border-radius: 16px 16px 0px 0px;\n max-height: 90vh;\n\n position: fixed;\n right: 0;\n left: 0;\n bottom: 0;\n }\n `\n }\n `,\n)\n\n// The scrollbar lives here so Container's border-radius can clip it.\nconst ScrollArea = styled.div`\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior-y: none;\n`\n\nconst Header = styled(Box)<{ $sticky: boolean }>(\n ({ $sticky, theme }) => css`\n padding: ${theme.space[300]} ${theme.space[200]} ${theme.space[200]};\n\n ${\n $sticky &&\n css`\n position: sticky;\n top: 0;\n z-index: 1;\n background: ${theme.color.background[100]};\n `\n }\n `,\n)\n\nconst ContentArea = styled(Box)<{ $hasFooter: boolean }>(\n ({ $hasFooter, theme }) => css`\n padding: 0 ${theme.space[200]} ${$hasFooter ? '0' : theme.space[300]};\n `,\n)\n\nconst Footer = styled.div<{ $sticky: boolean }>(\n ({ $sticky, theme }) => css`\n display: flex;\n gap: ${theme.space[100]};\n padding: ${theme.space[300]} ${theme.space[200]};\n\n & > * {\n flex: 1;\n }\n\n ${\n $sticky &&\n css`\n position: sticky;\n bottom: 0;\n z-index: 1;\n background: ${theme.color.background[100]};\n `\n }\n `,\n)\n\nconst TitleElements = styled(Box)`\n align-self: center;\n`\n"]}
1
+ {"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../src/Modal/Modal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { FC, ReactNode, useEffect, useId, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport styled, { css, useTheme } from 'styled-components'\n\nimport { Box } from '../Box'\nimport { Text, type TextProps } from '../Text'\nimport { useBodyScrollLock } from '../hooks/useBodyScrollLock'\nimport { IconContainer } from '../sharedStyles/shared.styles'\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\nimport { faXmark } from '@awesome.me/kit-46ca99185c/icons/classic/regular'\n\ninterface IModalContainer {\n // drawer state\n $drawer: boolean\n // modal width\n $width: string\n}\n\nexport type ModalProps = {\n /**\n * Title of the modal\n * @default \"\" (empty string)\n *\n * @example\n * ```tsx\n * <Modal title=\"MultiCar Account\" />\n * ```\n *\n * @example\n * ```tsx\n * <Modal title={{ typo: 'hero', children: 'MultiCar Account' }} />\n * ```\n */\n title?: string | TitleProps\n icon?: string\n children?: ReactNode\n rightPanel?: ReactNode\n showModal?: boolean\n handleClick: () => void\n drawer?: boolean\n cross?: boolean\n width?: string\n containerClass?: string\n portalContainer?: Element | DocumentFragment\n closeOnOverlayClick?: boolean\n /**\n * Keeps the title and close button visible while the content scrolls.\n * @default false\n */\n stickyHeader?: boolean\n /**\n * Rendered below the content, e.g. one or two buttons, which share the\n * width equally. Put the primary action last.\n */\n footer?: ReactNode\n /**\n * Keeps `footer` visible while the content scrolls.\n * @default false\n */\n stickyFooter?: boolean\n}\n\nexport type TitleProps = TextProps\n\nconst getDefaultTitleProps = (title: string): TitleProps => ({\n children: title,\n tag: 'h2',\n typo: 'heading-small',\n align: 'left',\n})\n\nconst FOCUSABLE_SELECTOR =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nexport const Modal: FC<ModalProps> = ({\n title = '',\n children,\n rightPanel,\n showModal = false,\n handleClick,\n drawer = true,\n cross = true,\n width,\n containerClass,\n portalContainer = document.body,\n closeOnOverlayClick = true,\n stickyHeader = false,\n footer,\n stickyFooter = false,\n}) => {\n const [modalNode, setModalNode] = useState<HTMLDivElement | null>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const titleId = useId()\n const theme = useTheme()\n\n useBodyScrollLock({ node: modalNode, showModal })\n\n // Record the opener while rendering the open state, before any child can take focus.\n const [wasOpen, setWasOpen] = useState(false)\n const [opener, setOpener] = useState<HTMLElement | null>(null)\n if (showModal !== wasOpen) {\n setWasOpen(showModal)\n if (showModal) setOpener(document.activeElement as HTMLElement | null)\n }\n\n const latestRef = useRef({ handleClick, closeOnOverlayClick })\n useEffect(() => {\n latestRef.current = { handleClick, closeOnOverlayClick }\n })\n\n useEffect(() => {\n const container = containerRef.current\n if (!showModal || !container) return\n\n // A child with autoFocus may already have focus, so leave it there.\n if (!container.contains(document.activeElement)) container.focus()\n\n const handleKeyDown = (event: KeyboardEvent) => {\n const active = document.activeElement\n // Only the modal that has focus responds, so stacked modals close one at a time.\n if (!container.contains(active)) return\n\n if (event.key === 'Escape') {\n if (event.defaultPrevented || event.isComposing) return\n if (latestRef.current.closeOnOverlayClick) {\n latestRef.current.handleClick()\n }\n return\n }\n\n if (event.key !== 'Tab') return\n\n const focusable =\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n\n if (!first) {\n event.preventDefault()\n } else if (event.shiftKey && (active === first || active === container)) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n opener?.focus()\n }\n }, [showModal, opener])\n\n const isTitleString = typeof title === 'string'\n const titleProps = isTitleString ? getDefaultTitleProps(title) : title\n const labelId = titleProps.id ?? titleId\n\n if (!showModal) return null\n\n return createPortal(\n <Wrapper ref={setModalNode}>\n <Overlay\n onClick={() => closeOnOverlayClick && handleClick()}\n $closeOnOverlayClick={closeOnOverlayClick}\n />\n <Container\n ref={containerRef}\n tabIndex={-1}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? labelId : undefined}\n $drawer={drawer}\n $width={width || '460px'}\n className={containerClass}\n >\n <ScrollArea>\n <Header\n flex\n alignItems=\"flex-start\"\n justifyContent=\"space-between\"\n $sticky={stickyHeader}\n >\n <TitleElements flex direction=\"column\">\n <Text {...titleProps} id={labelId} />\n </TitleElements>\n <Box flex alignItems=\"center\" gap={'space.100'}>\n {rightPanel}\n {cross && (\n <IconContainer\n as=\"button\"\n onClick={handleClick}\n role=\"button\"\n title=\"Close modal\"\n $size={32}\n style={{\n background: theme.color.illustration.neutral[300],\n borderRadius: '100%',\n padding: '6px',\n border: 'none',\n cursor: 'pointer',\n }}\n >\n <FontAwesomeIcon\n icon={faXmark}\n color={theme.color.icon.base}\n />\n </IconContainer>\n )}\n </Box>\n </Header>\n <ContentArea flex direction=\"column\" $hasFooter={!!footer}>\n {children}\n </ContentArea>\n {footer ? <Footer $sticky={stickyFooter}>{footer}</Footer> : null}\n </ScrollArea>\n </Container>\n </Wrapper>,\n portalContainer,\n )\n}\n\nconst Wrapper = styled(Box)`\n display: flex;\n position: absolute;\n z-index: 999;\n top: 0;\n left: 0;\n height: 100vh;\n width: 100%;\n justify-content: center;\n align-items: center;\n`\n\nconst Overlay = styled.div<{ $closeOnOverlayClick: boolean }>`\n position: fixed;\n background: ${({ theme }) => theme.color.surface.base[900]};\n cursor: ${(props) => (props.$closeOnOverlayClick ? 'pointer' : 'default')};\n opacity: 0.4;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n`\n\nconst Container = styled.div<IModalContainer>(\n ({ $drawer, $width }) => css`\n display: flex;\n flex-direction: column;\n background: ${({ theme }) => theme.color.background[100]};\n box-sizing: border-box;\n border-radius: 16px;\n width: 100%;\n max-width: ${$width};\n position: fixed;\n max-height: calc(100vh - 64px);\n overflow: hidden;\n outline: none;\n transition: all 0.3s ease-in-out;\n\n ${\n $drawer === true &&\n css`\n @media (max-width: 768px) {\n max-width: none;\n border-radius: 16px 16px 0px 0px;\n max-height: 90vh;\n\n position: fixed;\n right: 0;\n left: 0;\n bottom: 0;\n }\n `\n }\n `,\n)\n\n// The scrollbar lives here so Container's border-radius can clip it.\nconst ScrollArea = styled.div`\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior-y: none;\n`\n\nconst Header = styled(Box)<{ $sticky: boolean }>(\n ({ $sticky, theme }) => css`\n padding: ${theme.space[300]} ${theme.space[200]} ${theme.space[200]};\n\n ${\n $sticky &&\n css`\n position: sticky;\n top: 0;\n z-index: 1;\n background: ${theme.color.background[100]};\n `\n }\n `,\n)\n\nconst ContentArea = styled(Box)<{ $hasFooter: boolean }>(\n ({ $hasFooter, theme }) => css`\n padding: 0 ${theme.space[200]} ${$hasFooter ? '0' : theme.space[300]};\n `,\n)\n\nconst Footer = styled.div<{ $sticky: boolean }>(\n ({ $sticky, theme }) => css`\n display: flex;\n gap: ${theme.space[100]};\n padding: ${theme.space[300]} ${theme.space[200]};\n\n & > * {\n flex: 1;\n }\n\n ${\n $sticky &&\n css`\n position: sticky;\n bottom: 0;\n z-index: 1;\n background: ${theme.color.background[100]};\n `\n }\n `,\n)\n\nconst TitleElements = styled(Box)`\n align-self: center;\n`\n"]}
@@ -1,25 +1,30 @@
1
1
  import { useEffect } from 'react';
2
- import { clearAllBodyScrollLocks, disableBodyScroll, enableBodyScroll, } from 'body-scroll-lock';
2
+ import { clearAllBodyScrollLocks, disableBodyScroll } from 'body-scroll-lock';
3
+ // body-scroll-lock restores the body on every unlock, even while other locks
4
+ // remain, so count open locks and only release the body when the last closes.
5
+ let openLocks = 0;
3
6
  const enhancedDisabeBodyScroll = (node) => {
4
7
  disableBodyScroll(node, {
5
8
  reserveScrollBarGap: true,
6
9
  allowTouchMove: () => true,
7
10
  });
8
- document.body.style.top = `-${window.scrollY}px`;
11
+ if (openLocks === 0)
12
+ document.body.style.top = `-${window.scrollY}px`;
13
+ openLocks++;
14
+ };
15
+ const enhancedEnableBodyScroll = () => {
16
+ openLocks--;
17
+ if (openLocks > 0)
18
+ return;
19
+ clearAllBodyScrollLocks();
20
+ document.body.style.top = '';
9
21
  };
10
22
  export function useBodyScrollLock({ node, showModal, }) {
11
23
  useEffect(() => {
12
- return () => clearAllBodyScrollLocks();
13
- }, []);
14
- useEffect(() => {
15
- if (node === null)
24
+ if (node === null || !showModal)
16
25
  return;
17
- if (showModal) {
18
- enhancedDisabeBodyScroll(node);
19
- }
20
- else {
21
- enableBodyScroll(node);
22
- }
26
+ enhancedDisabeBodyScroll(node);
27
+ return enhancedEnableBodyScroll;
23
28
  }, [node, showModal]);
24
29
  }
25
30
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/useBodyScrollLock/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,wBAAwB,GAAG,CAAC,IAA2B,EAAE,EAAE;IAC/D,iBAAiB,CAAC,IAAI,EAAE;QACtB,mBAAmB,EAAE,IAAI;QACzB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;KAC3B,CAAC,CAAA;IAEF,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,OAAO,IAAI,CAAA;AAClD,CAAC,CAAA;AAED,MAAM,UAAU,iBAAiB,CAAC,EAChC,IAAI,EACJ,SAAS,GAIV;IACC,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE,CAAC,uBAAuB,EAAE,CAAA;IACxC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,KAAK,IAAI;YAAE,OAAM;QAEzB,IAAI,SAAS,EAAE,CAAC;YACd,wBAAwB,CAAC,IAAI,CAAC,CAAA;QAChC,CAAC;aAAM,CAAC;YACN,gBAAgB,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAA;AACvB,CAAC","sourcesContent":["import { useEffect } from 'react'\nimport {\n clearAllBodyScrollLocks,\n disableBodyScroll,\n enableBodyScroll,\n} from 'body-scroll-lock'\n\nconst enhancedDisabeBodyScroll = (node: HTMLElement | Element) => {\n disableBodyScroll(node, {\n reserveScrollBarGap: true,\n allowTouchMove: () => true,\n })\n\n document.body.style.top = `-${window.scrollY}px`\n}\n\nexport function useBodyScrollLock({\n node,\n showModal,\n}: {\n node: HTMLDivElement | null\n showModal: boolean\n}) {\n useEffect(() => {\n return () => clearAllBodyScrollLocks()\n }, [])\n\n useEffect(() => {\n if (node === null) return\n\n if (showModal) {\n enhancedDisabeBodyScroll(node)\n } else {\n enableBodyScroll(node)\n }\n }, [node, showModal])\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/useBodyScrollLock/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAE7E,6EAA6E;AAC7E,8EAA8E;AAC9E,IAAI,SAAS,GAAG,CAAC,CAAA;AAEjB,MAAM,wBAAwB,GAAG,CAAC,IAA2B,EAAE,EAAE;IAC/D,iBAAiB,CAAC,IAAI,EAAE;QACtB,mBAAmB,EAAE,IAAI;QACzB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;KAC3B,CAAC,CAAA;IAEF,IAAI,SAAS,KAAK,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,OAAO,IAAI,CAAA;IACrE,SAAS,EAAE,CAAA;AACb,CAAC,CAAA;AAED,MAAM,wBAAwB,GAAG,GAAG,EAAE;IACpC,SAAS,EAAE,CAAA;IACX,IAAI,SAAS,GAAG,CAAC;QAAE,OAAM;IAEzB,uBAAuB,EAAE,CAAA;IACzB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAA;AAC9B,CAAC,CAAA;AAED,MAAM,UAAU,iBAAiB,CAAC,EAChC,IAAI,EACJ,SAAS,GAIV;IACC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,SAAS;YAAE,OAAM;QAEvC,wBAAwB,CAAC,IAAI,CAAC,CAAA;QAE9B,OAAO,wBAAwB,CAAA;IACjC,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAA;AACvB,CAAC","sourcesContent":["import { useEffect } from 'react'\nimport { clearAllBodyScrollLocks, disableBodyScroll } from 'body-scroll-lock'\n\n// body-scroll-lock restores the body on every unlock, even while other locks\n// remain, so count open locks and only release the body when the last closes.\nlet openLocks = 0\n\nconst enhancedDisabeBodyScroll = (node: HTMLElement | Element) => {\n disableBodyScroll(node, {\n reserveScrollBarGap: true,\n allowTouchMove: () => true,\n })\n\n if (openLocks === 0) document.body.style.top = `-${window.scrollY}px`\n openLocks++\n}\n\nconst enhancedEnableBodyScroll = () => {\n openLocks--\n if (openLocks > 0) return\n\n clearAllBodyScrollLocks()\n document.body.style.top = ''\n}\n\nexport function useBodyScrollLock({\n node,\n showModal,\n}: {\n node: HTMLDivElement | null\n showModal: boolean\n}) {\n useEffect(() => {\n if (node === null || !showModal) return\n\n enhancedDisabeBodyScroll(node)\n\n return enhancedEnableBodyScroll\n }, [node, showModal])\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrshmllw/smores-react",
3
- "version": "16.6.0",
3
+ "version": "16.7.0",
4
4
  "description": "Collection of React components used by Marshmallow Technology",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",