Skip to content

Commit

Permalink
Merge pull request #345 from MovieReviewComment/feature/issue-302/sti…
Browse files Browse the repository at this point in the history
…cky-ios

[#302] Implement useStickyIOS
  • Loading branch information
2wheeh authored May 2, 2024
2 parents 480b087 + a623f12 commit be0c445
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
1 change: 1 addition & 0 deletions ui/src/app/(board)/review/write/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ReviewTitle from '@/components/review/client/review-title';

import { EditorRefProvider } from '@/context/editor/editor-ref-context';
import { ReviewProvider } from '@/context/review/review-context';

import { MAX_CONTENT_LENGTH } from '@/lib/constants/review';

const Editor = dynamic(() => import('@/editor'), {
Expand Down
89 changes: 89 additions & 0 deletions ui/src/hooks/common/use-sticky-ios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// This is a custom hook to fix the issue with sticky and the soft keyboard on ios.
// on ios, when the soft keyboard is open, the keyboard pushes the content up.
// This ends up getting the sticky element out of the screen.
// As a workaround, we wrap the sticky element with a wrapper,
// set a margin to the sticky element as much as the wrapper is out of the screen.
import { useDebouncedCallback } from './use-debounced-callback';
import { useCallback, useEffect, useRef } from 'react';

const DEBOUNCE_TIME = 150; // ms
const TRANSITION_CLASS = 'animate-slide-down';

export function useStickyIOS<
StickyElementType extends HTMLElement = HTMLDivElement,
WrapperElementType extends HTMLElement = HTMLDivElement,
>() {
const stickyRef = useRef<StickyElementType>(null);
const wrapperRef = useRef<WrapperElementType>(null);
const blurred = useRef(false);

const stickyOffsetRef = useRef(0);

const setMargin = () => {
const wrapper = wrapperRef.current;
const sticky = stickyRef.current;

if (!wrapper || !sticky) {
return;
}

const newPosition = wrapper.getBoundingClientRect().top;
// do nothing if wrapper remains in the screen
if (newPosition >= 0) {
return;
}

stickyRef.current.classList.add(TRANSITION_CLASS);
// if wrapper is out of the screen,
// add a margin to show the sticky element
stickyOffsetRef.current = Math.abs(newPosition);

// set the margin by the new fixed position
sticky.style.marginTop = `${stickyOffsetRef.current}px`;
};

const debouncedSetMargin = useDebouncedCallback(setMargin, DEBOUNCE_TIME);

const resetPosition = useCallback(() => {
if (stickyRef.current) {
stickyRef.current.style.marginTop = '0px';
stickyRef.current.classList.remove(TRANSITION_CLASS);
}

if (stickyOffsetRef.current > 0) {
stickyOffsetRef.current = 0;
}
}, []);

const handleScroll = useCallback(() => {
// put the sticky element back to default position
resetPosition();

// When the keyboard gets closed, both the 'scroll' and 'blur' events are fired.
// 'scroll' => 'blur' or 'blur' => 'scroll'
// We skip handling here when 'scroll' is fired after 'blur'.
// Otherwise, wrapper.getBoundingClientRect().top might return incorrect value
// due to keyboard closing animation.
// This would set wrong marginTop with gap above the sticky element.
if (blurred.current) {
blurred.current = false;
return;
}
// show the sticky element with delay
debouncedSetMargin();
}, [debouncedSetMargin, resetPosition]);

useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [handleScroll]);

const handleBlur = useCallback(() => {
blurred.current = true;
resetPosition();
}, [resetPosition]);

return { handleBlur, stickyRef, wrapperRef };
}
5 changes: 5 additions & 0 deletions ui/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const config: Config = {
extend: {
animation: {
'slide-up': 'slide-up 0.3s ease-in-out both',
'slide-down': 'slide-down 0.3s ease-in-out both',
},
keyframes: {
shimmer: {
Expand All @@ -17,6 +18,10 @@ const config: Config = {
'0%': { transform: 'translateY(30%)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
'slide-down': {
'0%': { transform: 'translateY(-100%)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
Expand Down

0 comments on commit be0c445

Please sign in to comment.