@skyscanner/backpack-web 42.4.0 → 42.5.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.
@@ -15,4 +15,4 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
- .bpk-bottom-sheet{z-index:1100;width:100%;max-width:32rem;margin:auto;transform:scale(1);transition:opacity 200ms ease-in-out,transform 200ms ease-in-out;outline:0;background-color:#fff;opacity:1;overflow:hidden;overflow-y:scroll;-ms-overflow-style:none;scrollbar-width:none;-webkit-tap-highlight-color:rgba(0,0,0,0);box-shadow:0px 12px 50px 0px rgba(37,32,31,.25);border-radius:.5rem}@media(max-width: 32rem){.bpk-bottom-sheet{position:fixed;bottom:0;height:fit-content;max-height:90%;margin-bottom:0;border-radius:1.5rem 1.5rem 0 0;overflow-x:hidden}}.bpk-bottom-sheet::-webkit-scrollbar{display:none}.bpk-bottom-sheet--appear{animation-duration:200ms;animation-name:slide-up;animation-timing-function:ease-in-out}@media(min-width: 32.0625rem){.bpk-bottom-sheet--appear{transform:scale(0.9);opacity:0;animation:none}}@media(min-width: 32.0625rem){.bpk-bottom-sheet--appear-active{transform:scale(1);opacity:1}}.bpk-bottom-sheet--exit{animation-fill-mode:forwards;animation-duration:200ms;animation-name:slide-down;animation-timing-function:ease-in-out}@media(min-width: 32.0625rem){.bpk-bottom-sheet--exit{animation:none}}.bpk-bottom-sheet--content{padding:0;flex:1;overflow-y:auto}.bpk-bottom-sheet--padding-base-top{padding-top:1rem}.bpk-bottom-sheet--padding-base-bottom{padding-bottom:1rem}.bpk-bottom-sheet--padding-base-start{padding-inline-start:1rem}.bpk-bottom-sheet--padding-base-end{padding-inline-end:1rem}.bpk-bottom-sheet--padding-lg-top{padding-top:1.5rem}.bpk-bottom-sheet--padding-lg-bottom{padding-bottom:1.5rem}.bpk-bottom-sheet--padding-lg-start{padding-inline-start:1.5rem}.bpk-bottom-sheet--padding-lg-end{padding-inline-end:1.5rem}.bpk-bottom-sheet--padding-xxl-top{padding-top:2.5rem}.bpk-bottom-sheet--padding-xxl-bottom{padding-bottom:2.5rem}.bpk-bottom-sheet--padding-xxl-start{padding-inline-start:2.5rem}.bpk-bottom-sheet--padding-xxl-end{padding-inline-end:2.5rem}.bpk-bottom-sheet--padding-xxxl-top{padding-top:4rem}.bpk-bottom-sheet--padding-xxxl-bottom{padding-bottom:4rem}.bpk-bottom-sheet--padding-xxxl-start{padding-inline-start:4rem}.bpk-bottom-sheet--padding-xxxl-end{padding-inline-end:4rem}@media(min-width: 32.0625rem){.bpk-bottom-sheet--wide{max-width:64rem}}.bpk-bottom-sheet--header-wrapper{position:sticky;top:0;z-index:899;background-color:#fff;background-color:var(--bpk-navigation-bar-background-color, rgb(255, 255, 255))}.bpk-bottom-sheet--header{min-height:fit-content;padding:1.5rem}@keyframes slide-up{0%{transform:translateY(100%)}100%{transform:translateY(0%)}}@keyframes slide-down{0%{transform:translateY(0%)}100%{transform:translateY(100%)}}
18
+ .bpk-bottom-sheet{z-index:1100;width:100%;max-width:32rem;margin:auto;transition:opacity 200ms ease-in-out,transform 200ms ease-in-out;outline:0;background-color:#fff;opacity:1;overflow:hidden;overflow-y:scroll;-ms-overflow-style:none;scrollbar-width:none;-webkit-tap-highlight-color:rgba(0,0,0,0);box-shadow:0px 12px 50px 0px rgba(37,32,31,.25);border-radius:.5rem}@media(max-width: 32rem){.bpk-bottom-sheet{position:fixed;bottom:0;height:fit-content;max-height:90%;margin-bottom:0;border-radius:1.5rem 1.5rem 0 0;overflow-x:hidden}}.bpk-bottom-sheet::-webkit-scrollbar{display:none}.bpk-bottom-sheet--appear{animation-duration:200ms;animation-name:slide-up;animation-timing-function:ease-in-out}@media(min-width: 32.0625rem){.bpk-bottom-sheet--appear{transform:scale(0.9);opacity:0;animation:none}}@media(min-width: 32.0625rem){.bpk-bottom-sheet--appear-active{transform:scale(1);opacity:1}}.bpk-bottom-sheet--exit{animation-fill-mode:forwards;animation-duration:200ms;animation-name:slide-down;animation-timing-function:ease-in-out}@media(min-width: 32.0625rem){.bpk-bottom-sheet--exit{animation:none}}.bpk-bottom-sheet--content{padding:0;flex:1;overflow-y:auto}.bpk-bottom-sheet--padding-base-top{padding-top:1rem}.bpk-bottom-sheet--padding-base-bottom{padding-bottom:1rem}.bpk-bottom-sheet--padding-base-start{padding-inline-start:1rem}.bpk-bottom-sheet--padding-base-end{padding-inline-end:1rem}.bpk-bottom-sheet--padding-lg-top{padding-top:1.5rem}.bpk-bottom-sheet--padding-lg-bottom{padding-bottom:1.5rem}.bpk-bottom-sheet--padding-lg-start{padding-inline-start:1.5rem}.bpk-bottom-sheet--padding-lg-end{padding-inline-end:1.5rem}.bpk-bottom-sheet--padding-xxl-top{padding-top:2.5rem}.bpk-bottom-sheet--padding-xxl-bottom{padding-bottom:2.5rem}.bpk-bottom-sheet--padding-xxl-start{padding-inline-start:2.5rem}.bpk-bottom-sheet--padding-xxl-end{padding-inline-end:2.5rem}.bpk-bottom-sheet--padding-xxxl-top{padding-top:4rem}.bpk-bottom-sheet--padding-xxxl-bottom{padding-bottom:4rem}.bpk-bottom-sheet--padding-xxxl-start{padding-inline-start:4rem}.bpk-bottom-sheet--padding-xxxl-end{padding-inline-end:4rem}@media(min-width: 32.0625rem){.bpk-bottom-sheet--wide{max-width:64rem}}.bpk-bottom-sheet--header-wrapper{position:sticky;top:0;z-index:899;background-color:#fff;background-color:var(--bpk-navigation-bar-background-color, rgb(255, 255, 255))}.bpk-bottom-sheet--header{min-height:fit-content;padding:1.5rem}@keyframes slide-up{0%{transform:translateY(100%)}100%{transform:translateY(0%)}}@keyframes slide-down{0%{transform:translateY(0%)}100%{transform:translateY(100%)}}
@@ -1,2 +1,5 @@
1
1
  import BpkCardList from './src/BpkCardList';
2
+ import { LAYOUTS, ACCESSORY_DESKTOP_TYPES, ACCESSORY_MOBILE_TYPES } from './src/common-types';
2
3
  export default BpkCardList;
4
+ export { LAYOUTS, ACCESSORY_DESKTOP_TYPES, ACCESSORY_MOBILE_TYPES };
5
+ export type { LayoutDesktop, LayoutMobile } from './src/common-types';
@@ -17,4 +17,6 @@
17
17
  */
18
18
 
19
19
  import BpkCardList from "./src/BpkCardList";
20
- export default BpkCardList;
20
+ import { LAYOUTS, ACCESSORY_DESKTOP_TYPES, ACCESSORY_MOBILE_TYPES } from "./src/common-types";
21
+ export default BpkCardList;
22
+ export { LAYOUTS, ACCESSORY_DESKTOP_TYPES, ACCESSORY_MOBILE_TYPES };
@@ -4,4 +4,5 @@ import BpkInfoBannerExpandable from './src/BpkInfoBannerExpandable';
4
4
  import { ALERT_TYPES, STYLE_TYPES } from './src/common-types';
5
5
  import withBannerAlertState from './src/withBannerAlertState';
6
6
  export { ALERT_TYPES, STYLE_TYPES, BpkInfoBannerDismissable, BpkInfoBannerExpandable, withBannerAlertState, };
7
+ export type { AlertTypeValue, StyleTypeValue } from './src/common-types';
7
8
  export default BpkInfoBanner;
@@ -19,10 +19,9 @@
19
19
  import { useEffect, useState } from 'react';
20
20
  import { Dialog } from '@ark-ui/react';
21
21
  import { durationBase } from '@skyscanner/bpk-foundations-web/tokens/base.es6';
22
- import { getDataComponentAttribute } from "../../../../bpk-react-utils";
22
+ import { getDataComponentAttribute, useBodyLock } from "../../../../bpk-react-utils";
23
23
  import { ModalTypeProvider } from "../BpkModalV3Context";
24
24
  import { MODAL_V3_TYPES } from "../common-types";
25
- import useBodyLock from "./useBodyLock";
26
25
  import { jsx as _jsx } from "react/jsx-runtime";
27
26
  const BpkModalV3Root = ({
28
27
  children,
@@ -1,3 +1,4 @@
1
1
  import component, { TEXT_COLORS, TEXT_STYLES } from './src/BpkText';
2
2
  export default component;
3
3
  export { TEXT_COLORS, TEXT_STYLES };
4
+ export type { Tag, TextStyle, TextColor } from './src/BpkText';
@@ -8,10 +8,11 @@ import { getDataComponentAttribute } from './src/getDataComponentAttribute';
8
8
  import isRTL from './src/isRTL';
9
9
  import { setNativeValue } from './src/nativeEventHandler';
10
10
  import { SURFACE_COLORS } from './src/surfaceColors';
11
+ import useBodyLock from './src/useBodyLock';
11
12
  import withDefaultProps from './src/withDefaultProps';
12
13
  import wrapDisplayName from './src/wrapDisplayName';
13
14
  export type { SurfaceBgColor } from './src/surfaceColors';
14
- export { Portal, TransitionInitialMount, cssModules, deprecated, withDefaultProps, wrapDisplayName, isDeviceIphone, isDeviceIpad, isDeviceIos, isRTL, BpkDialogWrapper, setNativeValue, getDataComponentAttribute, SURFACE_COLORS, };
15
+ export { Portal, TransitionInitialMount, cssModules, deprecated, withDefaultProps, wrapDisplayName, isDeviceIphone, isDeviceIpad, isDeviceIos, isRTL, BpkDialogWrapper, setNativeValue, getDataComponentAttribute, SURFACE_COLORS, useBodyLock, };
15
16
  declare const _default: {
16
17
  Portal: typeof Portal;
17
18
  TransitionInitialMount: ({ appearActiveClassName, appearClassName, children, transitionTimeout, }: {
@@ -55,5 +56,6 @@ declare const _default: {
55
56
  readonly surfaceLowContrast: "surface-low-contrast";
56
57
  readonly surfaceTint: "surface-tint";
57
58
  };
59
+ useBodyLock: (isLocked: boolean) => void;
58
60
  };
59
61
  export default _default;
@@ -27,9 +27,10 @@ import { getDataComponentAttribute } from "./src/getDataComponentAttribute";
27
27
  import isRTL from "./src/isRTL";
28
28
  import { setNativeValue } from "./src/nativeEventHandler";
29
29
  import { SURFACE_COLORS } from "./src/surfaceColors";
30
+ import useBodyLock from "./src/useBodyLock";
30
31
  import withDefaultProps from "./src/withDefaultProps";
31
32
  import wrapDisplayName from "./src/wrapDisplayName";
32
- export { Portal, TransitionInitialMount, cssModules, deprecated, withDefaultProps, wrapDisplayName, isDeviceIphone, isDeviceIpad, isDeviceIos, isRTL, BpkDialogWrapper, setNativeValue, getDataComponentAttribute, SURFACE_COLORS };
33
+ export { Portal, TransitionInitialMount, cssModules, deprecated, withDefaultProps, wrapDisplayName, isDeviceIphone, isDeviceIpad, isDeviceIos, isRTL, BpkDialogWrapper, setNativeValue, getDataComponentAttribute, SURFACE_COLORS, useBodyLock };
33
34
  export default {
34
35
  Portal,
35
36
  TransitionInitialMount,
@@ -44,5 +45,6 @@ export default {
44
45
  BpkDialogWrapper,
45
46
  setNativeValue,
46
47
  getDataComponentAttribute,
47
- SURFACE_COLORS
48
+ SURFACE_COLORS,
49
+ useBodyLock
48
50
  };
@@ -20,20 +20,12 @@ import { useEffect, useRef, useState } from 'react';
20
20
  import CSSTransition from 'react-transition-group/CSSTransition';
21
21
  import cssModules from "../cssModules";
22
22
  import { getDataComponentAttribute } from "../getDataComponentAttribute";
23
+ import useBodyLock from "../useBodyLock";
23
24
  import STYLES from "./BpkDialogWrapper.module.css";
24
25
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
25
26
  const getClassName = cssModules(STYLES);
26
27
  // TODO: this check if the browser support the HTML dialog element. We can remove it once we drop support as a business for Safari 14
27
28
  const dialogSupported = typeof HTMLDialogElement === 'function';
28
- const setPageProperties = ({
29
- isDialogOpen
30
- }) => {
31
- document.body.style.overflowY = isDialogOpen ? 'hidden' : 'visible';
32
- if (!dialogSupported) {
33
- document.body.style.position = isDialogOpen ? 'fixed' : 'relative';
34
- document.body.style.width = isDialogOpen ? '100%' : 'auto';
35
- }
36
- };
37
29
  export const BpkDialogWrapper = ({
38
30
  children,
39
31
  closeOnEscPressed = false,
@@ -100,18 +92,7 @@ export const BpkDialogWrapper = ({
100
92
  window.removeEventListener('keydown', handleKeyDown);
101
93
  };
102
94
  }, [id, isOpen, onClose, closeOnEscPressed, closeOnScrimClick]);
103
-
104
- // Lock the scroll of the page when the dialog is open
105
- useEffect(() => {
106
- setPageProperties({
107
- isDialogOpen: isOpen
108
- });
109
- return () => {
110
- setPageProperties({
111
- isDialogOpen: false
112
- });
113
- };
114
- }, [isOpen]);
95
+ useBodyLock(isOpen);
115
96
  const aria = {
116
97
  ...('ariaLabelledby' in ariaProps ? {
117
98
  'aria-labelledby': ariaProps.ariaLabelledby
@@ -2,6 +2,11 @@
2
2
  * Locks body scroll when the modal is open, preventing background scroll and
3
3
  * iOS Safari bounce effects. Restores all body styles and scroll position on
4
4
  * cleanup.
5
+ *
6
+ * Uses reference counting so multiple concurrent callers (e.g. Modal + BottomSheet)
7
+ * coordinate correctly: styles are locked on the first caller and only restored
8
+ * when the last caller unlocks.
9
+ *
5
10
  * @param {boolean} isLocked - Whether the body scroll should be locked.
6
11
  * @returns {void}
7
12
  */
@@ -16,18 +16,25 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- import { useEffect, useRef } from 'react';
19
+ import { useEffect } from 'react';
20
+ // Module-level shared state for reference counting across concurrent callers
21
+ let lockCount = 0;
22
+ let savedBodyStyles = null;
23
+ let savedScrollY = 0;
20
24
 
21
25
  /**
22
26
  * Locks body scroll when the modal is open, preventing background scroll and
23
27
  * iOS Safari bounce effects. Restores all body styles and scroll position on
24
28
  * cleanup.
29
+ *
30
+ * Uses reference counting so multiple concurrent callers (e.g. Modal + BottomSheet)
31
+ * coordinate correctly: styles are locked on the first caller and only restored
32
+ * when the last caller unlocks.
33
+ *
25
34
  * @param {boolean} isLocked - Whether the body scroll should be locked.
26
35
  * @returns {void}
27
36
  */
28
37
  const useBodyLock = isLocked => {
29
- const savedScrollYRef = useRef(0);
30
- const savedBodyStylesRef = useRef(null);
31
38
  useEffect(() => {
32
39
  if (!isLocked) {
33
40
  return undefined;
@@ -35,33 +42,36 @@ const useBodyLock = isLocked => {
35
42
  const {
36
43
  body
37
44
  } = document;
38
- const currentScrollY = window.scrollY;
39
- savedScrollYRef.current = currentScrollY;
40
- savedBodyStylesRef.current = {
41
- overflow: body.style.overflow || '',
42
- position: body.style.position || '',
43
- top: body.style.top || '',
44
- width: body.style.width || '',
45
- touchAction: body.style.touchAction || '',
46
- overscrollBehavior: body.style.overscrollBehavior || ''
47
- };
48
- body.style.overflow = 'hidden';
49
- body.style.position = 'fixed';
50
- body.style.top = `-${currentScrollY}px`;
51
- body.style.width = '100%';
52
- body.style.touchAction = 'none';
53
- body.style.overscrollBehavior = 'contain';
45
+ if (lockCount === 0) {
46
+ savedScrollY = window.scrollY;
47
+ savedBodyStyles = {
48
+ overflow: body.style.overflow || '',
49
+ position: body.style.position || '',
50
+ top: body.style.top || '',
51
+ width: body.style.width || '',
52
+ touchAction: body.style.touchAction || '',
53
+ overscrollBehavior: body.style.overscrollBehavior || ''
54
+ };
55
+ body.style.overflow = 'hidden';
56
+ body.style.position = 'fixed';
57
+ body.style.top = `-${savedScrollY}px`;
58
+ body.style.width = '100%';
59
+ body.style.touchAction = 'none';
60
+ body.style.overscrollBehavior = 'contain';
61
+ }
62
+ lockCount += 1;
54
63
  return () => {
55
- if (savedBodyStylesRef.current) {
56
- const saved = savedBodyStylesRef.current;
64
+ lockCount -= 1;
65
+ if (lockCount === 0 && savedBodyStyles) {
66
+ const saved = savedBodyStyles;
57
67
  body.style.overflow = saved.overflow;
58
68
  body.style.position = saved.position;
59
69
  body.style.top = saved.top;
60
70
  body.style.width = saved.width;
61
71
  body.style.touchAction = saved.touchAction;
62
72
  body.style.overscrollBehavior = saved.overscrollBehavior;
63
- savedBodyStylesRef.current = null;
64
- window.scrollTo(0, savedScrollYRef.current);
73
+ savedBodyStyles = null;
74
+ window.scrollTo(0, savedScrollY);
65
75
  }
66
76
  };
67
77
  }, [isLocked]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyscanner/backpack-web",
3
- "version": "42.4.0",
3
+ "version": "42.5.1",
4
4
  "description": "Backpack Design System web library",
5
5
  "repository": {
6
6
  "type": "git",