@skyscanner/backpack-web 38.0.1 → 38.1.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.
@@ -25,7 +25,8 @@ import { ACCESSORY_DESKTOP_TYPES, ACCESSORY_MOBILE_TYPES, LAYOUTS } from "./comm
25
25
  import STYLES from "./BpkCardList.module.css";
26
26
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
27
27
  const getClassName = cssModules(STYLES);
28
- const DEFAULT_ITEMS = 3;
28
+ const DEFAULT_ITEMS_DESKTOP = 3;
29
+ const DEFAULT_ITEMS_MOBILE = 2;
29
30
  const BpkCardList = props => {
30
31
  const {
31
32
  accessibilityLabels,
@@ -37,8 +38,8 @@ const BpkCardList = props => {
37
38
  chipGroup,
38
39
  description,
39
40
  expandText,
40
- initiallyShownCardsDesktop = DEFAULT_ITEMS,
41
- initiallyShownCardsMobile = DEFAULT_ITEMS,
41
+ initiallyShownCardsDesktop = DEFAULT_ITEMS_DESKTOP,
42
+ initiallyShownCardsMobile = DEFAULT_ITEMS_MOBILE,
42
43
  layoutDesktop,
43
44
  layoutMobile,
44
45
  onButtonClick,
@@ -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-card-list{display:flex;flex-direction:column;gap:1.5rem}.bpk-card-list--card-list{display:flex;flex-direction:column}
18
+ .bpk-card-list{display:flex;flex-direction:column;gap:1.5rem}@media(max-width: 32rem){.bpk-card-list{gap:1rem}}.bpk-card-list--card-list{display:flex;flex-direction:column}
@@ -16,13 +16,15 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- import { useRef, useState, useEffect, isValidElement, Children } from 'react';
19
+ import { useRef, useState, useEffect, isValidElement, Children, useMemo } from 'react';
20
+ import throttle from 'lodash/throttle';
20
21
  import { cssModules } from "../../../bpk-react-utils";
21
22
  import { RENDER_BUFFER_SIZE } from "./constants";
22
- import { lockScroll, setA11yTabIndex, useUpdateCurrentIndexByVisibility, useScrollToCard, useIntersectionObserver } from "./utils";
23
+ import { lockScroll, setA11yTabIndex, useScrollToCard, useIntersectionObserver } from "./utils";
23
24
  import STYLES from "./BpkCardListCarousel.module.css";
24
25
  import { jsx as _jsx } from "react/jsx-runtime";
25
26
  const getClassName = cssModules(STYLES);
27
+ const PAGINATION_INDICATOR_MAX_SHOWN_COUNT = 5;
26
28
  const BpkCardListCarousel = props => {
27
29
  const {
28
30
  carouselLabel = (initiallyShownCards, childrenLength) => `Entering Carousel with ${initiallyShownCards} slides shown at a time, ${childrenLength} slides in total. Please use Pagination below with the Previous and Next buttons to navigate, or the slide dot buttons at the end to jump to slides.`,
@@ -39,7 +41,11 @@ const BpkCardListCarousel = props => {
39
41
  };
40
42
  const childrenLength = Children.count(children);
41
43
  const [root, setRoot] = useState(null);
44
+ const [, forceUpdate] = useState(0);
42
45
  const cardRefs = useRef([]);
46
+ const hasBeenVisibleRef = useRef(new Set());
47
+ const firstCardWidthRef = useRef(null);
48
+ const firstCardHeightRef = useRef(null);
43
49
  const [visibilityList, setVisibilityList] = useState(Array(childrenLength).fill(0));
44
50
  const stateScrollingLockRef = useRef(false);
45
51
  const openSetStateLockTimeoutRef = useRef(null);
@@ -47,6 +53,35 @@ const BpkCardListCarousel = props => {
47
53
  root,
48
54
  threshold: 0.5
49
55
  }, setVisibilityList);
56
+ useScrollToCard(currentIndex * initiallyShownCards, root, cardRefs, stateScrollingLockRef);
57
+
58
+ // Similar to Virtual Scrolling to improve performance
59
+ const firstVisibleIndex = Math.max(0, visibilityList.indexOf(1));
60
+ const lastVisibleIndex = firstVisibleIndex + initiallyShownCards - 1;
61
+ const dynamicRenderBufferSize = useMemo(() => {
62
+ if (childrenLength === 0 || initiallyShownCards === 0 || isMobile) return RENDER_BUFFER_SIZE;
63
+
64
+ // Calculate how many cards to render based on the number of initially shown cards and total children
65
+ const totalPages = Math.ceil(childrenLength / initiallyShownCards);
66
+ const shownIndicatorCount = Math.min(totalPages, PAGINATION_INDICATOR_MAX_SHOWN_COUNT);
67
+ return Math.max(RENDER_BUFFER_SIZE, (shownIndicatorCount - 1) * initiallyShownCards);
68
+ }, [childrenLength, initiallyShownCards, isMobile]);
69
+ const renderList = useMemo(() => visibilityList.map((_, index) => index >= firstVisibleIndex - dynamicRenderBufferSize && index <= lastVisibleIndex + dynamicRenderBufferSize ? 1 : 0), [visibilityList, firstVisibleIndex, lastVisibleIndex, dynamicRenderBufferSize]);
70
+ const cardRefFns = useMemo(() => Array(childrenLength).fill(null).map((_, i) => el => {
71
+ cardRefs.current[i] = el;
72
+ observerVisibility(el, i);
73
+ setA11yTabIndex(el, i, visibilityList);
74
+ // record the first card's width and height when it becomes visible
75
+ if (el && visibilityList[i] === 0) {
76
+ hasBeenVisibleRef.current.add(i);
77
+ if (firstCardWidthRef.current == null && el.offsetWidth) {
78
+ firstCardWidthRef.current = el.offsetWidth;
79
+ }
80
+ if (firstCardHeightRef.current == null && el.offsetHeight) {
81
+ firstCardHeightRef.current = el.offsetHeight;
82
+ }
83
+ }
84
+ }), [childrenLength, observerVisibility, visibilityList, hasBeenVisibleRef, firstCardWidthRef, firstCardHeightRef]);
50
85
  useEffect(() => {
51
86
  const container = root;
52
87
  if (isMobile || !container) return undefined;
@@ -63,13 +98,32 @@ const BpkCardListCarousel = props => {
63
98
  }
64
99
  };
65
100
  }, [root]);
66
- useUpdateCurrentIndexByVisibility(isMobile, visibilityList, setCurrentIndex, stateScrollingLockRef, openSetStateLockTimeoutRef);
67
- useScrollToCard(currentIndex, root, cardRefs, stateScrollingLockRef);
68
-
69
- // Similar to Virtual Scrolling to improve performance
70
- const firstVisibleIndex = Math.max(0, visibilityList.indexOf(1));
71
- const lastVisibleIndex = firstVisibleIndex + initiallyShownCards - 1;
72
- const renderList = visibilityList.map((_, index) => index >= firstVisibleIndex - RENDER_BUFFER_SIZE && index <= lastVisibleIndex + RENDER_BUFFER_SIZE ? 1 : 0);
101
+ useEffect(() => {
102
+ // update hasBeenVisibleRef to include the range of cards that should be visible
103
+ const start = currentIndex * initiallyShownCards;
104
+ const end = start + initiallyShownCards + dynamicRenderBufferSize;
105
+ for (let i = start; i < end && i < childrenLength; i += 1) {
106
+ hasBeenVisibleRef.current.add(i);
107
+ }
108
+ }, [currentIndex, initiallyShownCards, childrenLength, dynamicRenderBufferSize]);
109
+ useEffect(() => {
110
+ const firstVisible = visibilityList.indexOf(1);
111
+ if (firstVisible >= 0) {
112
+ const newIndex = Math.floor(firstVisible / initiallyShownCards);
113
+ if (newIndex !== currentIndex) {
114
+ setCurrentIndex(newIndex);
115
+ }
116
+ }
117
+ }, [initiallyShownCards]);
118
+ useEffect(() => {
119
+ const handleResize = throttle(() => {
120
+ firstCardWidthRef.current = null;
121
+ firstCardHeightRef.current = null;
122
+ forceUpdate(n => n + 1);
123
+ }, 200);
124
+ window.addEventListener('resize', handleResize);
125
+ return () => window.removeEventListener('resize', handleResize);
126
+ }, []);
73
127
  return /*#__PURE__*/_jsx("div", {
74
128
  className: getClassName(`bpk-card-list-row-rail__${layout}`),
75
129
  "data-testid": "bpk-card-list-row-rail__carousel",
@@ -82,23 +136,36 @@ const BpkCardListCarousel = props => {
82
136
  ref: setRoot,
83
137
  children: children.map((card, index) => {
84
138
  if (! /*#__PURE__*/isValidElement(card)) return null;
85
- const cardRefCallback = el => {
86
- cardRefs.current[index] = el;
87
- observerVisibility(el, index);
88
- setA11yTabIndex(el, index, visibilityList);
89
- };
90
- const cardStyle = isMobile ? {
91
- ...shownNumberStyle,
92
- visibility: renderList[index] === 1 ? 'visible' : 'hidden' // for mobile, {renderList[index] === 1 && card} will cause reflowing and bugs, implementing visibility improvement instead
93
- } : shownNumberStyle;
139
+ const cardDimensionStyle = {};
140
+ if (firstCardWidthRef.current) {
141
+ cardDimensionStyle.width = `${firstCardWidthRef.current}px`;
142
+ }
143
+ if (firstCardHeightRef.current) {
144
+ cardDimensionStyle.height = `${firstCardHeightRef.current}px`;
145
+ }
146
+
147
+ // Only render cards that are within the renderList range or have been visible before
148
+ if (renderList[index] !== 1 && !hasBeenVisibleRef.current.has(index)) {
149
+ return /*#__PURE__*/_jsx("div", {
150
+ style: {
151
+ ...shownNumberStyle,
152
+ ...cardDimensionStyle,
153
+ flexShrink: 0,
154
+ visibility: 'hidden'
155
+ },
156
+ "aria-hidden": "true"
157
+ }, `placeholder-${index.toString()}`);
158
+ }
94
159
  return /*#__PURE__*/_jsx("div", {
95
160
  className: getClassName(`bpk-card-list-row-rail__${layout}__card`),
96
- ref: cardRefCallback,
97
- style: cardStyle,
161
+ ref: cardRefFns[index],
162
+ style: {
163
+ ...shownNumberStyle,
164
+ ...cardDimensionStyle
165
+ },
98
166
  role: "group",
99
167
  "aria-label": slideLabel(index, childrenLength),
100
- "aria-current": index === currentIndex ? 'true' : 'false',
101
- children: isMobile ? card : renderList[index] === 1 && card
168
+ children: card
102
169
  }, `carousel-card-${index.toString()}`);
103
170
  })
104
171
  });
@@ -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-card-list-row-rail__row,.bpk-card-list-row-rail__rail{display:flex;padding-top:.25rem;padding-bottom:.25rem;overflow-x:scroll;box-sizing:border-box;gap:.25rem;scroll-snap-stop:always;scroll-snap-type:x mandatory;scrollbar-width:none}.bpk-card-list-row-rail__row::-webkit-scrollbar,.bpk-card-list-row-rail__rail::-webkit-scrollbar{display:none}.bpk-card-list-row-rail__row__card,.bpk-card-list-row-rail__rail__card{position:relative;padding:.5rem;flex:0 0 calc((100% - .5rem*(var(--initially-shown-cards, 3) - 1))/var(--initially-shown-cards, 3));overflow:visible;box-sizing:border-box;scroll-snap-align:start}.bpk-card-list-row-rail__rail{-webkit-overflow-scrolling:touch}
18
+ .bpk-card-list-row-rail__row,.bpk-card-list-row-rail__rail{display:flex;padding-top:.25rem;padding-bottom:1.5rem;overflow-x:hidden;box-sizing:border-box;gap:.25rem;scroll-snap-stop:always;scroll-snap-type:x mandatory;scrollbar-width:none}@media(max-width: 32rem){.bpk-card-list-row-rail__row,.bpk-card-list-row-rail__rail{padding-bottom:1rem;overflow-x:scroll}}.bpk-card-list-row-rail__row::-webkit-scrollbar,.bpk-card-list-row-rail__rail::-webkit-scrollbar{display:none}.bpk-card-list-row-rail__row__card,.bpk-card-list-row-rail__rail__card{position:relative;padding:0 .5rem;flex:0 0 calc((100% - .5rem*(var(--initially-shown-cards, 3) - 1))/var(--initially-shown-cards, 3));overflow:visible;box-sizing:border-box;scroll-snap-align:start}@media(max-width: 32rem){.bpk-card-list-row-rail__row__card,.bpk-card-list-row-rail__rail__card{flex:0 0 calc((100% - .5rem*(var(--initially-shown-cards, 3) - 1))/max(1,var(--initially-shown-cards, 3) - .8))}.bpk-card-list-row-rail__row__card:first-child,.bpk-card-list-row-rail__rail__card:first-child{padding-left:.25rem}html[dir=rtl] .bpk-card-list-row-rail__row__card:first-child,html[dir=rtl] .bpk-card-list-row-rail__rail__card:first-child{padding-right:.25rem;padding-left:.5rem}}.bpk-card-list-row-rail__rail{-webkit-overflow-scrolling:touch}
@@ -32,9 +32,10 @@ const BpkCardListRowRailContainer = props => {
32
32
  isMobile = false,
33
33
  layout
34
34
  } = props;
35
- const totalIndicators = Children.count(children);
35
+ const childrenCount = Children.count(children);
36
+ const totalIndicators = Math.ceil(childrenCount / initiallyShownCards);
37
+ const showAccessory = childrenCount > initiallyShownCards;
36
38
  const [currentIndex, setCurrentIndex] = useState(0);
37
- const showAccessory = totalIndicators > initiallyShownCards;
38
39
  const accessoryContent = layout === LAYOUTS.row && accessory === ACCESSORY_DESKTOP_TYPES.pagination ? /*#__PURE__*/_jsx(BpkPageIndicator, {
39
40
  currentIndex: currentIndex,
40
41
  totalIndicators: totalIndicators,
@@ -50,8 +51,8 @@ const BpkCardListRowRailContainer = props => {
50
51
  children: [/*#__PURE__*/_jsx(BpkCardListCarousel, {
51
52
  initiallyShownCards: initiallyShownCards,
52
53
  layout: layout,
53
- currentIndex: currentIndex,
54
54
  setCurrentIndex: setCurrentIndex,
55
+ currentIndex: currentIndex,
55
56
  isMobile: isMobile,
56
57
  carouselLabel: accessibilityLabels?.carouselLabel,
57
58
  slideLabel: accessibilityLabels?.slideLabel,
@@ -11,6 +11,5 @@ export declare const lockScroll: (stateScrollingLockRef: React.MutableRefObject<
11
11
  * @param index - Current container index
12
12
  */
13
13
  export declare const setA11yTabIndex: (el: HTMLDivElement | null, index: number, visibilityList: number[]) => void;
14
- export declare const useUpdateCurrentIndexByVisibility: (isMobile: boolean, visibilityList: number[], setCurrentIndex: (index: number) => void, stateScrollingLockRef: React.MutableRefObject<boolean>, openSetStateLockTimeoutRef: React.MutableRefObject<NodeJS.Timeout | null>) => void;
15
14
  export declare const useScrollToCard: (currentIndex: number, container: HTMLElement | null, cardRefs: React.MutableRefObject<Array<HTMLDivElement | null>>, stateScrollingLockRef: React.MutableRefObject<boolean>) => void;
16
15
  export declare const useIntersectionObserver: ({ root, threshold }: IntersectionObserverInit, setVisibilityList: React.Dispatch<React.SetStateAction<number[]>>) => (element: HTMLElement | null, index: number) => void;
@@ -16,7 +16,7 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- import { useEffect, useRef, useMemo } from 'react';
19
+ import { useEffect, useRef } from 'react';
20
20
  import { RELEASE_LOCK_DELAY } from "./constants";
21
21
 
22
22
  /**
@@ -53,17 +53,6 @@ export const setA11yTabIndex = (el, index, visibilityList) => {
53
53
  targetElement.tabIndex = visibilityList[index] === 1 ? 0 : -1;
54
54
  });
55
55
  };
56
- export const useUpdateCurrentIndexByVisibility = (isMobile, visibilityList, setCurrentIndex, stateScrollingLockRef, openSetStateLockTimeoutRef) => {
57
- useEffect(() => {
58
- if (isMobile) return; // No pagination on mobile, so no need to update the current index
59
- if (!visibilityList || visibilityList.length === 0) return;
60
- const firstVisibleIndex = visibilityList.findIndex(visibility => visibility === 1);
61
- if (firstVisibleIndex !== -1) {
62
- setCurrentIndex(firstVisibleIndex);
63
- lockScroll(stateScrollingLockRef, openSetStateLockTimeoutRef); // prevent scrollIntoView from being called immediately after the current index is set
64
- }
65
- }, [visibilityList]);
66
- };
67
56
  export const useScrollToCard = (currentIndex, container, cardRefs, stateScrollingLockRef) => {
68
57
  useEffect(() => {
69
58
  const isVisible = container && container.getBoundingClientRect().bottom > 0 && container.getBoundingClientRect().bottom <= window.innerHeight;
@@ -84,13 +73,10 @@ export const useIntersectionObserver = ({
84
73
  root,
85
74
  threshold
86
75
  }, setVisibilityList) => {
87
- const callbackRef = useRef(setVisibilityList);
76
+ const observerRef = useRef(null);
88
77
  useEffect(() => {
89
- callbackRef.current = setVisibilityList;
90
- });
91
- const observeAll = useMemo(() => {
92
78
  if (!root) return () => {};
93
- const observer = new IntersectionObserver(entries => {
79
+ observerRef.current = new IntersectionObserver(entries => {
94
80
  entries.forEach(entry => {
95
81
  const {
96
82
  index
@@ -115,13 +101,18 @@ export const useIntersectionObserver = ({
115
101
  root,
116
102
  threshold
117
103
  });
118
- const observeElement = (element, index) => {
119
- if (element && observer) {
120
- element.setAttribute('data-index', index.toString());
121
- observer.observe(element);
104
+ return () => {
105
+ if (observerRef.current) {
106
+ observerRef.current.disconnect();
107
+ observerRef.current = null;
122
108
  }
123
109
  };
124
- return observeElement;
125
- }, [root, threshold]);
126
- return observeAll;
110
+ }, [root, threshold, setVisibilityList]);
111
+ const observeElement = (element, index) => {
112
+ if (element && observerRef.current) {
113
+ element.setAttribute('data-index', index.toString());
114
+ observerRef.current.observe(element);
115
+ }
116
+ };
117
+ return observeElement;
127
118
  };
@@ -8,9 +8,9 @@ const BeachIcon = props => /*#__PURE__*/_jsxs("svg", {
8
8
  height: "1.5rem",
9
9
  ...props,
10
10
  children: [/*#__PURE__*/_jsx("path", {
11
- d: "M12 2c-4.376 0-9.038 4-9.96 8.016A1.71 1.71 0 0 0 3.857 12h.381a.815.815 0 0 0 .793-.62C5.703 8.642 7.745 3.39 12 2m0 0c4.255 1.39 6.297 6.642 6.969 9.38a.815.815 0 0 0 .793.62h.381a1.71 1.71 0 0 0 1.817-1.984C21.038 6 16.376 2 12 2"
11
+ d: "M13.201 6.622c.669 1.382 1.824 5.697 1.322 8.082-.09.428-.552.64-.967.489l-2.239-.816-1.698 4.666q.195.145.396.312c1.007.84 2.225 1.451 3.536 1.517.976.048 2.034.11 2.777.144.405.02.692.421.571.808a.08.08 0 0 1-.072.052H3.507A.507.507 0 0 1 3 21.37c0-.374.1-.743.307-1.054.944-1.424 2.359-2.93 4.535-2.238l1.596-4.384-2.02-.735c-.414-.15-.631-.609-.425-.996 1.148-2.15 4.807-4.712 6.208-5.341"
12
12
  }), /*#__PURE__*/_jsx("path", {
13
- d: "M12 2c-1.377 1.349-4.578 5.963-5.008 9.004a.896.896 0 0 0 .926.996h8.164a.897.897 0 0 0 .926-.996C16.578 7.963 13.377 3.349 12 2m0 11a1 1 0 0 0-1 1v7a1 1 0 0 0 2 0v-7a1 1 0 0 0-1-1"
13
+ d: "M13.201 6.622c3.29 1.197 5.709 5.454 5.313 8.7-.107.87-1.057 1.29-1.905.982l-.287-.104a.65.65 0 0 1-.427-.679c.238-2.225.127-6.699-2.694-8.9M3.538 9.872c1.783-2.742 6.373-4.448 9.663-3.25-3.576-.128-6.536 3.228-7.784 5.086a.65.65 0 0 1-.765.245l-.286-.105c-.847-.308-1.306-1.24-.828-1.976M19.356 7.57l.604.162a.623.623 0 0 1 .446.759.62.62 0 0 1-.761.44l-.605-.16a.624.624 0 0 1-.446-.76.62.62 0 0 1 .761-.44m-5.8-1.831a2.495 2.495 0 0 1 2.65-.928A2.495 2.495 0 0 1 17.99 7.85c-.083.316-.226.6-.41.847a10 10 0 0 0-4.025-2.957m5.475-1.241a.63.63 0 0 1 .853.226.616.616 0 0 1-.223.848l-.538.31a.63.63 0 0 1-.855-.225.62.62 0 0 1 .223-.849zm-5.981-1.584a.63.63 0 0 1 .854.225l.315.537a.617.617 0 0 1-.223.849.63.63 0 0 1-.854-.227l-.316-.537a.617.617 0 0 1 .224-.847m3.787-.5a.624.624 0 0 1 .446.76l-.158.599a.62.62 0 0 1-.762.44.624.624 0 0 1-.445-.76l.156-.6a.623.623 0 0 1 .763-.44"
14
14
  })]
15
15
  });
16
16
  export default BeachIcon;
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const TemperatureIcon = props => /*#__PURE__*/_jsxs("svg", {
4
+ xmlns: "http://www.w3.org/2000/svg",
5
+ viewBox: "0 0 24 24",
6
+ "aria-hidden": "true",
7
+ width: "1.5rem",
8
+ height: "1.5rem",
9
+ ...props,
10
+ children: [/*#__PURE__*/_jsx("path", {
11
+ d: "M10 7a1 1 0 0 1 1 1v4.82a.56.56 0 0 0 .325.492A3 3 0 0 1 13 16a3 3 0 1 1-4.325-2.688A.56.56 0 0 0 9 12.82V8a1 1 0 0 1 1-1"
12
+ }), /*#__PURE__*/_jsx("path", {
13
+ d: "M10 0a5 5 0 0 1 5 5v5.696c0 .262.105.511.277.708a7 7 0 1 1-10.555 0c.173-.197.278-.446.278-.708V5a5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3v6.53a1 1 0 0 1-.333.743 5 5 0 1 0 6.666 0A1 1 0 0 1 13 11.53V5a3 3 0 0 0-3-3",
14
+ clipRule: "evenodd"
15
+ }), /*#__PURE__*/_jsx("path", {
16
+ d: "M20 8a1 1 0 1 1 0 2h-3V8zm0-3a1 1 0 1 1 0 2h-3V5zm0-3a1 1 0 1 1 0 2h-3V2z"
17
+ })]
18
+ });
19
+ export default TemperatureIcon;
@@ -8,9 +8,9 @@ const BeachIcon = props => /*#__PURE__*/_jsxs("svg", {
8
8
  height: "1rem",
9
9
  ...props,
10
10
  children: [/*#__PURE__*/_jsx("path", {
11
- d: "M12 1.5c-4.595 0-9.49 4.2-10.458 8.416A1.796 1.796 0 0 0 3.45 12h.4a.856.856 0 0 0 .833-.651C5.388 8.474 7.533 2.959 12 1.5m0 0c4.468 1.46 6.612 6.974 7.317 9.849a.856.856 0 0 0 .833.651h.4a1.796 1.796 0 0 0 1.908-2.084C21.49 5.7 16.595 1.5 12 1.5"
11
+ d: "M12.237 7.688c.666 1.39 1.81 5.734 1.301 8.138-.091.433-.553.646-.968.495l-2.24-.816-1.677 4.612c1.098.787 2.35 1.355 3.7 1.355h2.071c.53 0 .958.429.958.958a.12.12 0 0 1-.12.12H2.007a.507.507 0 0 1-.507-.507c0-.374.1-.742.306-1.053 1.028-1.55 2.612-3.197 5.129-2.003l1.516-4.166-2.019-.733c-.415-.152-.63-.613-.423-1.002 1.155-2.17 4.824-4.761 6.228-5.398"
12
12
  }), /*#__PURE__*/_jsx("path", {
13
- d: "M12 1.5c-1.445 1.416-4.807 6.26-5.258 9.454A.94.94 0 0 0 7.714 12h8.572a.94.94 0 0 0 .972-1.046C16.808 7.761 13.445 2.916 12 1.5m0 12a1.5 1.5 0 0 0-1.5 1.5v6a1.5 1.5 0 0 0 3 0v-6a1.5 1.5 0 0 0-1.5-1.5"
13
+ d: "M12.237 7.688c3.29 1.197 5.7 5.48 5.294 8.75-.109.878-1.061 1.304-1.908.995l-.288-.104a.65.65 0 0 1-.426-.684c.245-2.242.146-6.748-2.672-8.957m-9.681 3.3c1.792-2.767 6.391-4.498 9.681-3.3-3.579-.12-6.55 3.27-7.804 5.144a.65.65 0 0 1-.766.25l-.288-.105c-.847-.308-1.304-1.247-.823-1.99m9.611-4.76A3.751 3.751 0 0 1 18.99 9.1a3.73 3.73 0 0 1-1.22 1.971c-1.044-2.111-2.928-3.875-5.396-4.774zm8.867 2.403.91.222a.937.937 0 0 1-.442 1.822l-.91-.22a.938.938 0 0 1 .442-1.823M20.46 4a.939.939 0 0 1 .976 1.601l-.8.488a.938.938 0 0 1-.976-1.6zm-9.02-2.186a.936.936 0 0 1 1.287.313l.49.8a.938.938 0 0 1-1.6.977l-.489-.8a.94.94 0 0 1 .312-1.29m5.67-.887c.504.122.812.63.69 1.133l-.219.91a.937.937 0 0 1-1.822-.44l.22-.913a.94.94 0 0 1 1.132-.69"
14
14
  })]
15
15
  });
16
16
  export default BeachIcon;
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const TemperatureIcon = props => /*#__PURE__*/_jsxs("svg", {
4
+ xmlns: "http://www.w3.org/2000/svg",
5
+ viewBox: "0 0 24 24",
6
+ "aria-hidden": "true",
7
+ width: "1rem",
8
+ height: "1rem",
9
+ ...props,
10
+ children: [/*#__PURE__*/_jsx("path", {
11
+ d: "M9 7.5A1.5 1.5 0 0 1 10.5 9v4.904a3 3 0 1 1-3 0V9A1.5 1.5 0 0 1 9 7.5"
12
+ }), /*#__PURE__*/_jsx("path", {
13
+ d: "M9 0a6 6 0 0 1 6 6v6c0 .75 1.5 2.812 1.5 4.5a7.5 7.5 0 0 1-15 0c0-1.688.75-3 1.5-4.5V6a6 6 0 0 1 6-6m0 3a3 3 0 0 0-3 3s.106 6.405 0 6.75-.037.3-.602 1.052A4.5 4.5 0 1 0 13.5 16.5c0-1.016-.49-1.946-1.055-2.698 0 0-.445-.661-.445-1.052V6a3 3 0 0 0-3-3",
14
+ clipRule: "evenodd"
15
+ }), /*#__PURE__*/_jsx("path", {
16
+ d: "M21 7.5a1.5 1.5 0 0 1 0 3h-3v-3zM21 3a1.5 1.5 0 0 1 0 3h-3V3z"
17
+ })]
18
+ });
19
+ export default TemperatureIcon;
@@ -131,9 +131,25 @@ const BpkPopover = ({
131
131
  children: target
132
132
  });
133
133
  const classNames = getClassName('bpk-popover', className);
134
- const bodyClassNames = getClassName(padded && 'bpk-popover__body--padded');
134
+ const bodyClassNames = getClassName('bpk-popover__body', padded && 'bpk-popover__body--padded');
135
135
  const labelId = `bpk-popover-label-${id}`;
136
136
  const renderElement = typeof renderTarget === 'function' ? renderTarget() : renderTarget;
137
+ const closeButtonIconElement = /*#__PURE__*/_jsx(BpkCloseButton, {
138
+ label: closeButtonText || closeButtonLabel,
139
+ onClick: event => {
140
+ bindEventSource(EVENT_SOURCES.CLOSE_BUTTON, onClose, event);
141
+ setIsOpenState(false);
142
+ },
143
+ ...closeButtonProps
144
+ });
145
+ const closeButtonTextElement = /*#__PURE__*/_jsx(BpkButtonLink, {
146
+ onClick: event => {
147
+ bindEventSource(EVENT_SOURCES.CLOSE_LINK, onClose, event);
148
+ setIsOpenState(false);
149
+ },
150
+ ...closeButtonProps,
151
+ children: closeButtonText
152
+ });
137
153
  return /*#__PURE__*/_jsxs(_Fragment, {
138
154
  children: [targetElement, isOpenState && /*#__PURE__*/_jsx(FloatingPortal, {
139
155
  root: renderElement,
@@ -173,28 +189,16 @@ const BpkPopover = ({
173
189
  id: labelId,
174
190
  textStyle: TEXT_STYLES.label1,
175
191
  children: label
176
- }), "\xA0", closeButtonIcon ? /*#__PURE__*/_jsx(BpkCloseButton, {
177
- label: closeButtonText || closeButtonLabel,
178
- onClick: event => {
179
- bindEventSource(EVENT_SOURCES.CLOSE_BUTTON, onClose, event);
180
- setIsOpenState(false);
181
- },
182
- ...closeButtonProps
183
- }) : closeButtonText && /*#__PURE__*/_jsx(BpkButtonLink, {
184
- onClick: event => {
185
- bindEventSource(EVENT_SOURCES.CLOSE_LINK, onClose, event);
186
- setIsOpenState(false);
187
- },
188
- ...closeButtonProps,
189
- children: closeButtonText
190
- })]
192
+ }), "\xA0", closeButtonIcon ? closeButtonIconElement : !!closeButtonText && closeButtonTextElement]
191
193
  }) : /*#__PURE__*/_jsx("span", {
192
194
  id: labelId,
193
195
  className: getClassName('bpk-popover__label'),
194
196
  children: label
195
- }), /*#__PURE__*/_jsx("div", {
197
+ }), /*#__PURE__*/_jsxs("div", {
196
198
  className: bodyClassNames,
197
- children: children
199
+ children: [/*#__PURE__*/_jsx("div", {
200
+ children: children
201
+ }), !labelAsTitle && closeButtonIcon && closeButtonIconElement]
198
202
  }), actionText && onAction && /*#__PURE__*/_jsx("div", {
199
203
  className: getClassName('bpk-popover__action'),
200
204
  children: /*#__PURE__*/_jsx(BpkButtonLink, {
@@ -203,14 +207,7 @@ const BpkPopover = ({
203
207
  })
204
208
  }), !labelAsTitle && closeButtonText && /*#__PURE__*/_jsx("footer", {
205
209
  className: getClassName('bpk-popover__footer'),
206
- children: /*#__PURE__*/_jsx(BpkButtonLink, {
207
- onClick: event => {
208
- bindEventSource(EVENT_SOURCES.CLOSE_LINK, onClose, event);
209
- setIsOpenState(false);
210
- },
211
- ...closeButtonProps,
212
- children: closeButtonText
213
- })
210
+ children: closeButtonTextElement
214
211
  })]
215
212
  })
216
213
  })
@@ -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-popover{transition:opacity 200ms ease-in-out;outline:0;background-color:#fff;opacity:1;border-radius:.5rem;box-shadow:0px 4px 14px 0px rgba(37,32,31,.25)}@media(min-width: 32.0625rem){.bpk-popover{max-width:32rem;transition:opacity 50ms ease-in-out}}.bpk-popover--appear{opacity:0}.bpk-popover--appear-active{opacity:1}.bpk-popover__arrow{width:1.5rem;height:1.5rem;fill:#fff}.bpk-popover__arrow[data-hide]{visibility:hidden}.bpk-popover__body--padded{padding:1rem}.bpk-popover__body--padded:not(:last-of-type){padding-bottom:0}.bpk-popover__header{display:flex;padding:1rem 1rem 0;justify-content:space-between}.bpk-popover__header~.bpk-popover__body--padded{padding-top:.5rem}.bpk-popover__label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;white-space:nowrap;overflow:hidden;clip:rect(0 0 0 0)}.bpk-popover__footer{padding:.5rem 1rem;text-align:right}html[dir=rtl] .bpk-popover__footer{text-align:left}.bpk-popover__action{padding:0 1rem 1rem 1rem}
18
+ .bpk-popover{transition:opacity 200ms ease-in-out;outline:0;background-color:#fff;opacity:1;border-radius:.5rem;box-shadow:0px 4px 14px 0px rgba(37,32,31,.25)}@media(min-width: 32.0625rem){.bpk-popover{max-width:32rem;transition:opacity 50ms ease-in-out}}.bpk-popover--appear{opacity:0}.bpk-popover--appear-active{opacity:1}.bpk-popover__arrow{width:1.5rem;height:1.5rem;fill:#fff}.bpk-popover__arrow[data-hide]{visibility:hidden}.bpk-popover__body{display:flex;column-gap:1rem;justify-content:space-between;align-items:flex-start}.bpk-popover__body--padded{padding:1rem}.bpk-popover__body--padded:not(:last-of-type){padding-bottom:0}.bpk-popover__header{display:flex;padding:1rem 1rem 0;justify-content:space-between}.bpk-popover__header~.bpk-popover__body--padded{padding-top:.5rem}.bpk-popover__label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;white-space:nowrap;overflow:hidden;clip:rect(0 0 0 0)}.bpk-popover__footer{padding:.5rem 1rem;text-align:right}html[dir=rtl] .bpk-popover__footer{text-align:left}.bpk-popover__action{padding:0 1rem 1rem 1rem}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyscanner/backpack-web",
3
- "version": "38.0.1",
3
+ "version": "38.1.1",
4
4
  "description": "Backpack Design System web library",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,8 +27,8 @@
27
27
  "@radix-ui/react-compose-refs": "^1.1.1",
28
28
  "@radix-ui/react-slider": "1.1.2",
29
29
  "@react-google-maps/api": "^2.19.3",
30
- "@skyscanner/bpk-foundations-web": "^19.5.0",
31
- "@skyscanner/bpk-svgs": "^20.6.0",
30
+ "@skyscanner/bpk-foundations-web": "^22.1.0",
31
+ "@skyscanner/bpk-svgs": "^20.8.0",
32
32
  "a11y-focus-scope": "^1.1.3",
33
33
  "a11y-focus-store": "^1.0.0",
34
34
  "d3-path": "^3.1.0",