@skyscanner/backpack-web 42.23.0-dev-v26810456218.1 → 42.24.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.
@@ -38,6 +38,7 @@ const BpkCardList = props => {
38
38
  chipGroup,
39
39
  description,
40
40
  expandText,
41
+ initiallyInViewCardIndex = 0,
41
42
  initiallyShownCardsDesktop = DEFAULT_ITEMS_DESKTOP,
42
43
  initiallyShownCardsMobile = DEFAULT_ITEMS_MOBILE,
43
44
  layoutDesktop,
@@ -78,6 +79,7 @@ const BpkCardList = props => {
78
79
  initiallyShownCards: initiallyShownCardsMobile,
79
80
  layout: layoutMobile,
80
81
  accessibilityLabels: accessibilityLabels,
82
+ initiallyInViewCardIndex: initiallyInViewCardIndex,
81
83
  isMobile: true,
82
84
  children: cardList
83
85
  }), layoutMobile === LAYOUTS.stack && /*#__PURE__*/_jsx(BpkCardListGridStack, {
@@ -97,6 +99,7 @@ const BpkCardList = props => {
97
99
  initiallyShownCards: initiallyShownCardsDesktop,
98
100
  layout: layoutDesktop,
99
101
  accessibilityLabels: accessibilityLabels,
102
+ initiallyInViewCardIndex: initiallyInViewCardIndex,
100
103
  children: cardList
101
104
  }), layoutDesktop === LAYOUTS.grid && accessoryDesktop !== ACCESSORY_DESKTOP_TYPES.pagination && /*#__PURE__*/_jsx(BpkCardListGridStack, {
102
105
  accessory: accessoryDesktop,
@@ -30,6 +30,7 @@ const BpkCardListCarousel = props => {
30
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.`,
31
31
  children,
32
32
  currentIndex,
33
+ initialPageIndex,
33
34
  initiallyShownCards,
34
35
  isMobile = false,
35
36
  layout,
@@ -58,7 +59,8 @@ const BpkCardListCarousel = props => {
58
59
  cardRefs,
59
60
  visibilityList,
60
61
  container: root,
61
- enabled: !isMobile
62
+ enabled: !isMobile,
63
+ initialPageIndex
62
64
  });
63
65
 
64
66
  // Similar to Virtual Scrolling to improve performance
@@ -28,6 +28,7 @@ const BpkCardListRowRailContainer = props => {
28
28
  accessibilityLabels,
29
29
  accessory,
30
30
  children,
31
+ initiallyInViewCardIndex,
31
32
  initiallyShownCards,
32
33
  isMobile = false,
33
34
  layout
@@ -35,7 +36,18 @@ const BpkCardListRowRailContainer = props => {
35
36
  const childrenCount = Children.count(children);
36
37
  const totalIndicators = Math.ceil(childrenCount / initiallyShownCards);
37
38
  const showAccessory = childrenCount > initiallyShownCards;
38
- const [currentIndex, setCurrentIndex] = useState(0);
39
+
40
+ // Calculate initial page from card index
41
+ const [initialPageIndex] = useState(() => {
42
+ if (initiallyInViewCardIndex < 0) {
43
+ return 0;
44
+ }
45
+ if (initiallyInViewCardIndex >= childrenCount) {
46
+ return Math.max(0, totalIndicators - 1);
47
+ }
48
+ return Math.floor(initiallyInViewCardIndex / initiallyShownCards);
49
+ });
50
+ const [currentIndex, setCurrentIndex] = useState(initialPageIndex);
39
51
  const accessoryContent = layout === LAYOUTS.row && accessory === ACCESSORY_DESKTOP_TYPES.pagination ? /*#__PURE__*/_jsx(BpkPageIndicator, {
40
52
  currentIndex: currentIndex,
41
53
  totalIndicators: totalIndicators,
@@ -57,6 +69,7 @@ const BpkCardListRowRailContainer = props => {
57
69
  isMobile: isMobile,
58
70
  carouselLabel: accessibilityLabels?.carouselLabel,
59
71
  slideLabel: accessibilityLabels?.slideLabel,
72
+ initialPageIndex: initialPageIndex,
60
73
  children: children
61
74
  }), accessoryContent && showAccessory && /*#__PURE__*/_jsx("div", {
62
75
  role: "region",
@@ -15,6 +15,7 @@ type UsePageScrollSyncOptions = {
15
15
  visibilityList: number[];
16
16
  container: HTMLElement | null;
17
17
  enabled: boolean;
18
+ initialPageIndex: number;
18
19
  };
19
20
  /**
20
21
  * Provides bidirectional synchronisation between page index state and scroll position.
@@ -27,5 +28,5 @@ type UsePageScrollSyncOptions = {
27
28
  * The hook uses a lock mechanism to prevent conflicts between programmatic and
28
29
  * user-initiated scrolling.
29
30
  */
30
- export declare const usePageScrollSync: ({ cardRefs, container, currentIndex, enabled, initiallyShownCards, setCurrentIndex, visibilityList, }: UsePageScrollSyncOptions) => void;
31
+ export declare const usePageScrollSync: ({ cardRefs, container, currentIndex, enabled, initialPageIndex, initiallyShownCards, setCurrentIndex, visibilityList, }: UsePageScrollSyncOptions) => void;
31
32
  export {};
@@ -98,6 +98,7 @@ export const usePageScrollSync = ({
98
98
  container,
99
99
  currentIndex,
100
100
  enabled,
101
+ initialPageIndex,
101
102
  initiallyShownCards,
102
103
  setCurrentIndex,
103
104
  visibilityList
@@ -105,6 +106,26 @@ export const usePageScrollSync = ({
105
106
  const isUserScrollingRef = useRef(false);
106
107
  const scrollEndTimeoutRef = useRef(null);
107
108
  const lastCurrentIndexRef = useRef(currentIndex);
109
+ const hasInitialScrolled = useRef(false);
110
+
111
+ // Effect 0: Initial scroll (instant, runs once on mount)
112
+ useEffect(() => {
113
+ if (hasInitialScrolled.current) {
114
+ return;
115
+ }
116
+ hasInitialScrolled.current = true;
117
+ if (!enabled || initialPageIndex === 0) {
118
+ return;
119
+ }
120
+ const targetCard = cardRefs.current?.[initialPageIndex * initiallyShownCards];
121
+ if (targetCard) {
122
+ targetCard.scrollIntoView({
123
+ behavior: 'instant',
124
+ block: 'nearest',
125
+ inline: 'start'
126
+ });
127
+ }
128
+ }, [cardRefs, enabled, initialPageIndex, initiallyShownCards]); // Run once on mount only
108
129
 
109
130
  // Effect 1: Programmatic scroll when currentIndex changes
110
131
  useEffect(() => {
@@ -36,6 +36,7 @@ type CardListBaseProps = {
36
36
  accessoryMobile?: (typeof ACCESSORY_MOBILE_TYPES)[keyof typeof ACCESSORY_MOBILE_TYPES];
37
37
  initiallyShownCardsDesktop?: number;
38
38
  initiallyShownCardsMobile?: number;
39
+ initiallyInViewCardIndex?: number;
39
40
  chipGroup?: ReactElement;
40
41
  buttonContent?: React.ReactNode;
41
42
  onButtonClick?: () => void;
@@ -69,6 +70,7 @@ type CardListRowRailProps = {
69
70
  accessory?: typeof ACCESSORY_DESKTOP_TYPES.pagination;
70
71
  isMobile?: boolean;
71
72
  accessibilityLabels?: AccessibilityLabels;
73
+ initiallyInViewCardIndex: number;
72
74
  };
73
75
  type CardListCarouselProps = {
74
76
  children: Array<ReactElement<HTMLDivElement | HTMLAnchorElement>>;
@@ -79,6 +81,7 @@ type CardListCarouselProps = {
79
81
  isMobile?: boolean;
80
82
  carouselLabel?: (initiallyShownCards: number, childrenLength: number) => string;
81
83
  slideLabel?: (index: number, childrenLength: number) => string;
84
+ initialPageIndex: number;
82
85
  };
83
86
  type CardListProps = CardListBaseProps;
84
87
  export default CardListProps;
@@ -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-page-indicator-fullWidth__container{width:100%}.bpk-page-indicator{display:flex;width:100%;justify-content:center;align-items:center}.bpk-page-indicator__showNav{justify-content:space-between}.bpk-page-indicator__container{max-width:5rem;min-height:.5rem;overflow:hidden}.bpk-page-indicator__indicators-container{--direction: -1;transform:translateX(calc(var(--direction) * var(--scroll-index, 0) * 1rem));transition:transform 200ms ease-in-out;white-space:nowrap}html[dir=rtl] .bpk-page-indicator__indicators-container{--direction: 1}.bpk-page-indicator__indicator{display:inline-block;width:.5rem;height:.5rem;padding:0;border:none;border-radius:50%;margin-inline:.25rem}.bpk-page-indicator__indicator:hover{cursor:pointer}.bpk-page-indicator__indicator--default{background-color:#c1c7cf}.bpk-page-indicator__indicator--overImage,.bpk-page-indicator__indicator--overImageSpaced,.bpk-page-indicator__indicator--carousel{background-color:hsla(0,0%,100%,.5)}.bpk-page-indicator__indicator--active-default{background-color:#626971;pointer-events:none}.bpk-page-indicator__indicator--active-overImage,.bpk-page-indicator__indicator--active-overImageSpaced,.bpk-page-indicator__indicator--active-carousel{background-color:#fff;pointer-events:none}.bpk-page-indicator__nav-carousel{--bpk-button-border-radius: 50%;--bpk-button-secondary-on-dark-background-color: rgba(255, 255, 255, 0.5);--bpk-button-secondary-on-dark-hover-background-color: rgba(255, 255, 255, 0.8);--bpk-button-secondary-on-dark-active-background-color: rgba(255, 255, 255, 0.8);--bpk-button-secondary-on-dark-text-color: rgb(22, 22, 22);--bpk-button-secondary-on-dark-hover-text-color: rgb(22, 22, 22);--bpk-button-secondary-on-dark-active-text-color: rgb(22, 22, 22)}.bpk-page-indicator__nav-carousel-button{display:inline-flex;width:2rem;height:2rem;min-height:2rem;padding:0;justify-content:center;align-items:center}
18
+ .bpk-page-indicator-fullWidth__container{width:100%}.bpk-page-indicator{display:flex;width:100%;justify-content:center;align-items:center}.bpk-page-indicator__showNav{justify-content:space-between}.bpk-page-indicator__container{max-width:5rem;min-height:.5rem;overflow:hidden}.bpk-page-indicator__indicators-container{--direction: -1;transform:translateX(calc(var(--direction) * var(--scroll-index, 0) * 1rem));transition:transform 200ms ease-in-out;white-space:nowrap}html[dir=rtl] .bpk-page-indicator__indicators-container{--direction: 1}.bpk-page-indicator__indicator{display:inline-block;width:.5rem;height:.5rem;padding:0;border:none;border-radius:50%;margin-inline:.25rem}.bpk-page-indicator__indicator:hover{cursor:pointer}.bpk-page-indicator__indicator--default{background-color:#c1c7cf}.bpk-page-indicator__indicator--overImage,.bpk-page-indicator__indicator--overImageSpaced,.bpk-page-indicator__indicator--carousel{background-color:hsla(0,0%,100%,.5)}.bpk-page-indicator__indicator--active-default{background-color:#626971;pointer-events:none}.bpk-page-indicator__indicator--active-overImage,.bpk-page-indicator__indicator--active-overImageSpaced,.bpk-page-indicator__indicator--active-carousel{background-color:#fff;pointer-events:none}.bpk-page-indicator__nav-carousel{--bpk-button-border-radius: 50%;--bpk-button-secondary-on-dark-background-color: rgba(255, 255, 255, 0.5);--bpk-button-secondary-on-dark-hover-background-color: rgba(255, 255, 255, 0.8);--bpk-button-secondary-on-dark-active-background-color: rgba(255, 255, 255, 0.8);--bpk-button-secondary-on-dark-text-color: rgb(22, 22, 22);--bpk-button-secondary-on-dark-hover-text-color: rgb(22, 22, 22);--bpk-button-secondary-on-dark-active-text-color: rgb(22, 22, 22)}.bpk-page-indicator__nav-carousel .bpk-page-indicator__nav-carousel-button{display:inline-flex;width:2rem;height:2rem;min-height:2rem;padding:0;justify-content:center;align-items:center}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyscanner/backpack-web",
3
- "version": "42.23.0-dev-v26810456218.1",
3
+ "version": "42.24.0",
4
4
  "description": "Backpack Design System web library",
5
5
  "repository": {
6
6
  "type": "git",