@skyscanner/backpack-web 38.1.0 → 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.
@@ -24,6 +24,7 @@ import { lockScroll, setA11yTabIndex, useScrollToCard, useIntersectionObserver }
24
24
  import STYLES from "./BpkCardListCarousel.module.css";
25
25
  import { jsx as _jsx } from "react/jsx-runtime";
26
26
  const getClassName = cssModules(STYLES);
27
+ const PAGINATION_INDICATOR_MAX_SHOWN_COUNT = 5;
27
28
  const BpkCardListCarousel = props => {
28
29
  const {
29
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.`,
@@ -32,6 +33,7 @@ const BpkCardListCarousel = props => {
32
33
  initiallyShownCards,
33
34
  isMobile = false,
34
35
  layout,
36
+ setCurrentIndex,
35
37
  slideLabel = (index, childrenLength) => `slide ${index + 1} of ${childrenLength}`
36
38
  } = props;
37
39
  const shownNumberStyle = {
@@ -56,7 +58,15 @@ const BpkCardListCarousel = props => {
56
58
  // Similar to Virtual Scrolling to improve performance
57
59
  const firstVisibleIndex = Math.max(0, visibilityList.indexOf(1));
58
60
  const lastVisibleIndex = firstVisibleIndex + initiallyShownCards - 1;
59
- const renderList = useMemo(() => visibilityList.map((_, index) => index >= firstVisibleIndex - RENDER_BUFFER_SIZE && index <= lastVisibleIndex + RENDER_BUFFER_SIZE ? 1 : 0), [visibilityList, firstVisibleIndex, lastVisibleIndex]);
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]);
60
70
  const cardRefFns = useMemo(() => Array(childrenLength).fill(null).map((_, i) => el => {
61
71
  cardRefs.current[i] = el;
62
72
  observerVisibility(el, i);
@@ -91,11 +101,20 @@ const BpkCardListCarousel = props => {
91
101
  useEffect(() => {
92
102
  // update hasBeenVisibleRef to include the range of cards that should be visible
93
103
  const start = currentIndex * initiallyShownCards;
94
- const end = start + initiallyShownCards + RENDER_BUFFER_SIZE;
104
+ const end = start + initiallyShownCards + dynamicRenderBufferSize;
95
105
  for (let i = start; i < end && i < childrenLength; i += 1) {
96
106
  hasBeenVisibleRef.current.add(i);
97
107
  }
98
- }, [currentIndex, initiallyShownCards, childrenLength]);
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]);
99
118
  useEffect(() => {
100
119
  const handleResize = throttle(() => {
101
120
  firstCardWidthRef.current = null;
@@ -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: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{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}
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}
@@ -51,6 +51,7 @@ const BpkCardListRowRailContainer = props => {
51
51
  children: [/*#__PURE__*/_jsx(BpkCardListCarousel, {
52
52
  initiallyShownCards: initiallyShownCards,
53
53
  layout: layout,
54
+ setCurrentIndex: setCurrentIndex,
54
55
  currentIndex: currentIndex,
55
56
  isMobile: isMobile,
56
57
  carouselLabel: accessibilityLabels?.carouselLabel,
@@ -1,4 +1,4 @@
1
- import type { ReactElement } from 'react';
1
+ import type { ReactElement, Dispatch, SetStateAction } from 'react';
2
2
  declare const LAYOUTS: {
3
3
  readonly grid: "grid";
4
4
  readonly stack: "stack";
@@ -70,6 +70,7 @@ type CardListCarouselProps = {
70
70
  initiallyShownCards: number;
71
71
  layout: typeof LAYOUTS.row | typeof LAYOUTS.rail;
72
72
  currentIndex: number;
73
+ setCurrentIndex: Dispatch<SetStateAction<number>>;
73
74
  isMobile?: boolean;
74
75
  carouselLabel?: (initiallyShownCards: number, childrenLength: number) => string;
75
76
  slideLabel?: (index: number, childrenLength: number) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyscanner/backpack-web",
3
- "version": "38.1.0",
3
+ "version": "38.1.1",
4
4
  "description": "Backpack Design System web library",
5
5
  "repository": {
6
6
  "type": "git",