From cfc1c38f69578c85a79ac753a73930483f791f53 Mon Sep 17 00:00:00 2001 From: Kristian Antrobus Date: Fri, 8 Nov 2024 10:08:53 -0600 Subject: [PATCH 1/3] fix(tabs): non scroll to end --- .changeset/healthy-squids-cheat.md | 8 ++++++ .../code-block/src/CodeBlockTabList.tsx | 4 +-- .../components/code-block/src/utlis.ts | 25 ++++++++++++------- .../src/InPageNavigation.tsx | 4 +-- .../in-page-navigation/src/utils.ts | 25 ++++++++++++------- .../components/tabs/src/TabList.tsx | 8 ++++-- .../paste-core/components/tabs/src/utils.ts | 25 ++++++++++++------- 7 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 .changeset/healthy-squids-cheat.md diff --git a/.changeset/healthy-squids-cheat.md b/.changeset/healthy-squids-cheat.md new file mode 100644 index 0000000000..38d21e00c8 --- /dev/null +++ b/.changeset/healthy-squids-cheat.md @@ -0,0 +1,8 @@ +--- +"@twilio-paste/code-block": patch +"@twilio-paste/in-page-navigation": patch +"@twilio-paste/tabs": patch +"@twilio-paste/core": patch +--- + +[Tabs, CodeBlock, InPageNavigation] fixed a bug where items in the tabs list may not complete the scroll, still showing the overflow right button. \ No newline at end of file diff --git a/packages/paste-core/components/code-block/src/CodeBlockTabList.tsx b/packages/paste-core/components/code-block/src/CodeBlockTabList.tsx index f52228ef18..6e04daebaf 100644 --- a/packages/paste-core/components/code-block/src/CodeBlockTabList.tsx +++ b/packages/paste-core/components/code-block/src/CodeBlockTabList.tsx @@ -81,7 +81,7 @@ export const CodeBlockTabList = React.forwardRef - handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, listRef.current) + handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current) } visible={Boolean(elementOutOBoundsLeft)} element={element} @@ -116,7 +116,7 @@ export const CodeBlockTabList = React.forwardRef - handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, listRef.current) + handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current) } visible={Boolean(elementOutOBoundsRight)} element={element} diff --git a/packages/paste-core/components/code-block/src/utlis.ts b/packages/paste-core/components/code-block/src/utlis.ts index 3ba826e0d8..41ace5375d 100644 --- a/packages/paste-core/components/code-block/src/utlis.ts +++ b/packages/paste-core/components/code-block/src/utlis.ts @@ -17,7 +17,6 @@ export const useElementsOutOfBounds = (): { if (scrollContainer && listContainer) { const currentScrollContainerRightPosition = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().right; const currentScrollContainerXOffset = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().x; - let leftOutOfBounds: HTMLDivElement | null = null; let rightOutOfBounds: HTMLDivElement | null = null; @@ -34,10 +33,10 @@ export const useElementsOutOfBounds = (): { leftOutOfBounds = tab; } /** - * Compares the right side to the end of container with some buffer. Also ensure there are + * Compares the right side to the end of container. Also ensure there are * no value set as it loops through the array we don't want it to override the first value out of bounds. */ - if (right > currentScrollContainerRightPosition + 10 && !rightOutOfBounds) { + if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 1) && !rightOutOfBounds) { rightOutOfBounds = tab; } } @@ -76,12 +75,20 @@ export const handleScrollDirection = ( direction: "left" | "right", elementOutOBoundsLeft: HTMLDivElement | null, elementOutOBoundsRight: HTMLDivElement | null, - listContainer: HTMLElement | null, + scrollContainer: HTMLElement | null, ): void => { - if (listContainer) { - const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight; - if (elementToScrollTo) { - elementToScrollTo.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); - } + const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight; + + if (scrollContainer && elementToScrollTo) { + const elementRect = elementToScrollTo.getBoundingClientRect(); + const containerRect = scrollContainer.getBoundingClientRect(); + const containerScrollLeft = scrollContainer.scrollLeft; + + // Calculate the new scroll position + const newScrollLeft = + containerScrollLeft + (elementRect.left - containerRect.left) - containerRect.width / 2 + elementRect.width / 2; + + // Set the new scroll position + scrollContainer.scrollTo({ left: newScrollLeft, behavior: "smooth" }); } }; diff --git a/packages/paste-core/components/in-page-navigation/src/InPageNavigation.tsx b/packages/paste-core/components/in-page-navigation/src/InPageNavigation.tsx index 6edea06d89..6917393c25 100644 --- a/packages/paste-core/components/in-page-navigation/src/InPageNavigation.tsx +++ b/packages/paste-core/components/in-page-navigation/src/InPageNavigation.tsx @@ -196,7 +196,7 @@ const InPageNavigation = React.forwardRef - handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, listRef.current) + handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current) } visible={Boolean(elementOutOBoundsLeft)} element={element} @@ -233,7 +233,7 @@ const InPageNavigation = React.forwardRef - handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, listRef.current) + handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current) } visible={Boolean(elementOutOBoundsRight)} element={element} diff --git a/packages/paste-core/components/in-page-navigation/src/utils.ts b/packages/paste-core/components/in-page-navigation/src/utils.ts index 3ba826e0d8..41ace5375d 100644 --- a/packages/paste-core/components/in-page-navigation/src/utils.ts +++ b/packages/paste-core/components/in-page-navigation/src/utils.ts @@ -17,7 +17,6 @@ export const useElementsOutOfBounds = (): { if (scrollContainer && listContainer) { const currentScrollContainerRightPosition = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().right; const currentScrollContainerXOffset = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().x; - let leftOutOfBounds: HTMLDivElement | null = null; let rightOutOfBounds: HTMLDivElement | null = null; @@ -34,10 +33,10 @@ export const useElementsOutOfBounds = (): { leftOutOfBounds = tab; } /** - * Compares the right side to the end of container with some buffer. Also ensure there are + * Compares the right side to the end of container. Also ensure there are * no value set as it loops through the array we don't want it to override the first value out of bounds. */ - if (right > currentScrollContainerRightPosition + 10 && !rightOutOfBounds) { + if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 1) && !rightOutOfBounds) { rightOutOfBounds = tab; } } @@ -76,12 +75,20 @@ export const handleScrollDirection = ( direction: "left" | "right", elementOutOBoundsLeft: HTMLDivElement | null, elementOutOBoundsRight: HTMLDivElement | null, - listContainer: HTMLElement | null, + scrollContainer: HTMLElement | null, ): void => { - if (listContainer) { - const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight; - if (elementToScrollTo) { - elementToScrollTo.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); - } + const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight; + + if (scrollContainer && elementToScrollTo) { + const elementRect = elementToScrollTo.getBoundingClientRect(); + const containerRect = scrollContainer.getBoundingClientRect(); + const containerScrollLeft = scrollContainer.scrollLeft; + + // Calculate the new scroll position + const newScrollLeft = + containerScrollLeft + (elementRect.left - containerRect.left) - containerRect.width / 2 + elementRect.width / 2; + + // Set the new scroll position + scrollContainer.scrollTo({ left: newScrollLeft, behavior: "smooth" }); } }; diff --git a/packages/paste-core/components/tabs/src/TabList.tsx b/packages/paste-core/components/tabs/src/TabList.tsx index 3f577ffde4..b1634fb812 100644 --- a/packages/paste-core/components/tabs/src/TabList.tsx +++ b/packages/paste-core/components/tabs/src/TabList.tsx @@ -116,7 +116,9 @@ const HorizontalTabList: React.FC handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, ref.current)} + onClick={() => + handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current) + } visible={Boolean(elementOutOBoundsLeft)} element={element} showShadow={showShadow} @@ -147,7 +149,9 @@ const HorizontalTabList: React.FC handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, ref.current)} + onClick={() => + handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current) + } visible={Boolean(elementOutOBoundsRight)} element={element} showShadow={showShadow} diff --git a/packages/paste-core/components/tabs/src/utils.ts b/packages/paste-core/components/tabs/src/utils.ts index 838ea9e621..17febcdfec 100644 --- a/packages/paste-core/components/tabs/src/utils.ts +++ b/packages/paste-core/components/tabs/src/utils.ts @@ -23,7 +23,6 @@ export const useElementsOutOfBounds = (): { if (scrollContainer && listContainer) { const currentScrollContainerRightPosition = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().right; const currentScrollContainerXOffset = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().x; - let leftOutOfBounds: HTMLDivElement | null = null; let rightOutOfBounds: HTMLDivElement | null = null; @@ -40,10 +39,10 @@ export const useElementsOutOfBounds = (): { leftOutOfBounds = tab; } /** - * Compares the right side to the end of container with some buffer. Also ensure there are + * Compares the right side to the end of container. Also ensure there are * no value set as it loops through the array we don't want it to override the first value out of bounds. */ - if (right > currentScrollContainerRightPosition + 10 && !rightOutOfBounds) { + if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 1) && !rightOutOfBounds) { rightOutOfBounds = tab; } } @@ -82,12 +81,20 @@ export const handleScrollDirection = ( direction: "left" | "right", elementOutOBoundsLeft: HTMLDivElement | null, elementOutOBoundsRight: HTMLDivElement | null, - listContainer: HTMLElement | null, + scrollContainer: HTMLElement | null, ): void => { - if (listContainer) { - const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight; - if (elementToScrollTo) { - elementToScrollTo.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); - } + const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight; + + if (scrollContainer && elementToScrollTo) { + const elementRect = elementToScrollTo.getBoundingClientRect(); + const containerRect = scrollContainer.getBoundingClientRect(); + const containerScrollLeft = scrollContainer.scrollLeft; + + // Calculate the new scroll position + const newScrollLeft = + containerScrollLeft + (elementRect.left - containerRect.left) - containerRect.width / 2 + elementRect.width / 2; + + // Set the new scroll position + scrollContainer.scrollTo({ left: newScrollLeft, behavior: "smooth" }); } }; From cfe4d21fb39e539612df893011ad59b9ceb6cf61 Mon Sep 17 00:00:00 2001 From: Kristian Antrobus Date: Fri, 8 Nov 2024 11:23:36 -0600 Subject: [PATCH 2/3] fix(tabs): missing current check --- packages/paste-core/components/tabs/src/TabList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/paste-core/components/tabs/src/TabList.tsx b/packages/paste-core/components/tabs/src/TabList.tsx index b1634fb812..78d606f552 100644 --- a/packages/paste-core/components/tabs/src/TabList.tsx +++ b/packages/paste-core/components/tabs/src/TabList.tsx @@ -79,7 +79,7 @@ const HorizontalTabList: React.FC { - if (ref.current) { + if (ref.current && scrollableRef.current) { scrollableRef.current?.addEventListener("scroll", handleScrollEvent); window.addEventListener("resize", handleScrollEvent); determineElementsOutOfBounds(scrollableRef.current, ref.current); From 6e5e954297d4a4cb5eef881d1a56e807a49c911b Mon Sep 17 00:00:00 2001 From: Kristian Antrobus Date: Fri, 8 Nov 2024 12:20:09 -0600 Subject: [PATCH 3/3] fix(tabs): include button widths for comparison --- packages/paste-core/components/code-block/src/utlis.ts | 6 +++--- .../paste-core/components/in-page-navigation/src/utils.ts | 6 +++--- packages/paste-core/components/tabs/src/utils.ts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/paste-core/components/code-block/src/utlis.ts b/packages/paste-core/components/code-block/src/utlis.ts index 41ace5375d..9ee35e21f7 100644 --- a/packages/paste-core/components/code-block/src/utlis.ts +++ b/packages/paste-core/components/code-block/src/utlis.ts @@ -29,14 +29,14 @@ export const useElementsOutOfBounds = (): { * Compares the left side of the tab with the left side of the scrollable container position * as the x value will not be 0 due to being offset in the screen. */ - if (x < currentScrollContainerXOffset) { + if (Math.round(x) < Math.round(currentScrollContainerXOffset - 28)) { leftOutOfBounds = tab; } /** - * Compares the right side to the end of container. Also ensure there are + * Compares the right side to the end of container and button width. Also ensure there are * no value set as it loops through the array we don't want it to override the first value out of bounds. */ - if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 1) && !rightOutOfBounds) { + if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 28) && !rightOutOfBounds) { rightOutOfBounds = tab; } } diff --git a/packages/paste-core/components/in-page-navigation/src/utils.ts b/packages/paste-core/components/in-page-navigation/src/utils.ts index 41ace5375d..9ee35e21f7 100644 --- a/packages/paste-core/components/in-page-navigation/src/utils.ts +++ b/packages/paste-core/components/in-page-navigation/src/utils.ts @@ -29,14 +29,14 @@ export const useElementsOutOfBounds = (): { * Compares the left side of the tab with the left side of the scrollable container position * as the x value will not be 0 due to being offset in the screen. */ - if (x < currentScrollContainerXOffset) { + if (Math.round(x) < Math.round(currentScrollContainerXOffset - 28)) { leftOutOfBounds = tab; } /** - * Compares the right side to the end of container. Also ensure there are + * Compares the right side to the end of container and button width. Also ensure there are * no value set as it loops through the array we don't want it to override the first value out of bounds. */ - if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 1) && !rightOutOfBounds) { + if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 28) && !rightOutOfBounds) { rightOutOfBounds = tab; } } diff --git a/packages/paste-core/components/tabs/src/utils.ts b/packages/paste-core/components/tabs/src/utils.ts index 17febcdfec..812829a2fa 100644 --- a/packages/paste-core/components/tabs/src/utils.ts +++ b/packages/paste-core/components/tabs/src/utils.ts @@ -35,14 +35,14 @@ export const useElementsOutOfBounds = (): { * Compares the left side of the tab with the left side of the scrollable container position * as the x value will not be 0 due to being offset in the screen. */ - if (x < currentScrollContainerXOffset) { + if (Math.round(x) < Math.round(currentScrollContainerXOffset - 28)) { leftOutOfBounds = tab; } /** - * Compares the right side to the end of container. Also ensure there are + * Compares the right side to the end of container and button width. Also ensure there are * no value set as it loops through the array we don't want it to override the first value out of bounds. */ - if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 1) && !rightOutOfBounds) { + if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 28) && !rightOutOfBounds) { rightOutOfBounds = tab; } }