Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions packages/stems/src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,10 @@ export const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(
const id = useMemo(() => modalKey || uniqueId('modal-'), [modalKey])
const titleId = `${id}-title` || ariaLabelledbyProp
const subtitleId = `${id}-subtitle` || ariaDescribedbyProp
const [isDoneOpening, setIsDoneOpening] = useState(false)
const modalContextValue = useMemo(() => {
return { titleId, subtitleId, onClose }
}, [titleId, subtitleId, onClose])
return { titleId, subtitleId, onClose, isDoneOpening }
}, [titleId, subtitleId, onClose, isDoneOpening])

const onTouchMove = useCallback(
(e: any) => {
Expand Down Expand Up @@ -193,13 +194,24 @@ export const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(

const transition = useTransition(isOpen, null, {
from: { transform: 'scale(0)', opacity: 0 },
enter: { transform: 'scale(1)', opacity: 1 },
// @ts-ignore function is a valid value for enter, but the types don't acknowledge that
enter:
(item: boolean) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't tell without looking at docs what this was - in our case, this is isOpen and this works because we setIsDoneOpening(true) with a setImmediate so it executes next in the event stack?

Does our version of react-spring have onRest? I think that might make more sense: https://www.react-spring.dev/docs/advanced/events#onrest

@raymondjacobson raymondjacobson Sep 27, 2023

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on rest is only for spring not transition :/
we should change this to spring, but i didn't want to chew that off now.

but your analysis is correct!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, the docs for the new version say it's both! Must not be supported in our version

async (
next: (props: { transform: string; opacity: number }) => Promise<void>
) => {
await next({ transform: 'scale(1)', opacity: 1 })
if (item) {
setImmediate(() => setIsDoneOpening(true))
}
},
leave: { transform: 'scale(0)', opacity: 0 },
unique: true,
config: standard,
onDestroyed: () => {
if (!isOpen) {
setIsDestroyed(false)
setIsDoneOpening(false)
if (onClosed) {
onClosed()
}
Expand Down
5 changes: 5 additions & 0 deletions packages/stems/src/components/Modal/ModalContent.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { useContext } from 'react'

import cn from 'classnames'

import { Scrollbar } from 'components/Scrollbar'

import styles from './ModalContent.module.css'
import { ModalContext } from './ModalContext'
import { ModalContentProps } from './types'

/**
Expand All @@ -13,8 +16,10 @@ export const ModalContent = ({
children,
...props
}: ModalContentProps) => {
const { isDoneOpening } = useContext(ModalContext)
return (
<Scrollbar
isHidden={!isDoneOpening}
className={cn(styles.modalContentContainer, className)}
{...props}
>
Expand Down
1 change: 1 addition & 0 deletions packages/stems/src/components/Modal/ModalContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createContext } from 'react'
interface ModalContextValue {
titleId?: string
subtitleId?: string
isDoneOpening?: boolean
onClose?: () => void
}

Expand Down
111 changes: 59 additions & 52 deletions packages/stems/src/components/Scrollbar/Scrollbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useMemo } from 'react'
import { useEffect, useRef, useMemo, forwardRef, Ref, useCallback } from 'react'

import { ResizeObserver } from '@juggle/resize-observer'
import cn from 'classnames'
Expand All @@ -15,64 +15,71 @@ import { ScrollbarProps } from './types'
* `Scrollbar` uses react-perfect-scrollbar (https://www.npmjs.com/package/react-perfect-scrollbar)
* under the hood. For advanced use cases, refer to the documentation.
*/
export const Scrollbar = ({
children,
className,
id,
forward,
...props
}: ScrollbarProps) => {
// Do not remove:
// useMeasure ref is required for infinite scrolling to work
const [ref] = useMeasure({ polyfill: ResizeObserver })
const timerRef = useRef<NodeJS.Timeout | null>(null)
const elementId = useMemo(() => id || uniqueId('scrollbar-'), [id])
export const Scrollbar = forwardRef(
(
{ children, className, id, forward, isHidden, ...props }: ScrollbarProps,
forwardedRef: Ref<PerfectScrollbar>
) => {
// Do not remove:
// useMeasure ref is required for infinite scrolling to work
const [ref] = useMeasure({ polyfill: ResizeObserver })
const timerRef = useRef<NodeJS.Timeout | null>(null)
const elementId = useMemo(() => id || uniqueId('scrollbar-'), [id])

useEffect(() => {
return () => {
useEffect(() => {
return () => {
if (timerRef.current !== null) {
clearTimeout(timerRef.current)
}
}
}, [])

const hideScrollbar = useCallback(() => {
const element = document.getElementById(elementId)
if (element) {
element.classList.remove('scrollbar--hovered-visible')
}
if (timerRef.current !== null) {
clearTimeout(timerRef.current)
}
}
}, [])

const hideScrollbar = () => {
const element = document.getElementById(elementId)
if (element) {
element.classList.remove('scrollbar--hovered-visible')
}
if (timerRef.current !== null) {
clearTimeout(timerRef.current)
}
}
}, [elementId, timerRef])

const showScrollbar = () => {
const element = document.getElementById(elementId)
if (element) {
element.classList.add('scrollbar--hovered-visible')
}
if (timerRef.current !== null) {
clearTimeout(timerRef.current)
}
timerRef.current = setTimeout(() => {
const showScrollbar = useCallback(() => {
if (isHidden) return
const element = document.getElementById(elementId)
if (element) {
element.classList.remove('scrollbar--hovered-visible')
element.classList.add('scrollbar--hovered-visible')
}
}, 1400)
}
if (timerRef.current !== null) {
clearTimeout(timerRef.current)
}
timerRef.current = setTimeout(() => {
const element = document.getElementById(elementId)
if (element) {
element.classList.remove('scrollbar--hovered-visible')
}
}, 1400)
}, [elementId, timerRef, isHidden])

useEffect(() => {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the only diff in the file other than a forwardRef wrapper. Just this effect to call hideScrollbar on isHidden

if (isHidden) {
hideScrollbar()
}
}, [isHidden, hideScrollbar])

const content = forward ? children : <div ref={ref}>{children}</div>
const content = forward ? children : <div ref={ref}>{children}</div>

return (
<PerfectScrollbar
{...props}
id={elementId}
className={cn(styles.scrollbar, className)}
onMouseEnter={showScrollbar}
onMouseLeave={hideScrollbar}
>
{content}
</PerfectScrollbar>
)
}
return (
<PerfectScrollbar
{...props}
ref={forwardedRef}
id={elementId}
className={cn(styles.scrollbar, className)}
onMouseEnter={showScrollbar}
onMouseLeave={hideScrollbar}
>
{content}
</PerfectScrollbar>
)
}
)
6 changes: 6 additions & 0 deletions packages/stems/src/components/Scrollbar/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ import { ScrollBarProps as PerfectScrollbarProps } from 'react-perfect-scrollbar

export type ScrollbarProps = PerfectScrollbarProps & {
forward?: boolean
/**
* Whether or not to hide the scrollbars. Useful if you are rendering
* a scroll bar inside something that is changing sizes and you need
* to wait for the size to be stable before showing scrollbars.
*/
isHidden?: boolean
}