Skip to content

Commit

Permalink
refactor(base): expose ScrollContainer as package export in addition …
Browse files Browse the repository at this point in the history
…to part
  • Loading branch information
Benedicte Emilie Brækken authored and bjoerge committed Mar 4, 2021
1 parent 735d35f commit 0a755c9
Show file tree
Hide file tree
Showing 16 changed files with 123 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import CloseIcon from 'part:@sanity/base/close-icon'
import Button from 'part:@sanity/components/buttons/default'
import {ContainerQuery} from 'part:@sanity/components/container-query'
import styles from 'part:@sanity/components/dialogs/default-style'
import {ScrollContainer} from 'part:@sanity/components/scroll'
import React, {useCallback, useEffect, useState} from 'react'
import {LegacyLayerProvider} from '../../../../components'
import {LegacyLayerProvider, ScrollContainer} from '../../../../components'
import {useClickOutside} from '../hooks'
import {DefaultDialogActions} from './DefaultDialogActions'
import {DialogAction, DialogColor} from './types'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import CloseIcon from 'part:@sanity/base/close-icon'
import Button from 'part:@sanity/components/buttons/default'
import ButtonGrid from 'part:@sanity/components/buttons/button-grid'
import styles from 'part:@sanity/components/dialogs/fullscreen-style'
import {ScrollContainer} from 'part:@sanity/components/scroll'
import React, {useCallback, useEffect, useState} from 'react'
import {LegacyLayerProvider} from '../../../../components'
import {LegacyLayerProvider, ScrollContainer} from '../../../../components'
import {useClickOutside} from '../hooks'
import {DialogAction} from './types'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Button from 'part:@sanity/components/buttons/default'
import ButtonGrid from 'part:@sanity/components/buttons/button-grid'
import {Popover} from 'part:@sanity/components/popover'
import React, {useCallback, useEffect, useState} from 'react'
import {ScrollContainer} from '../../../../components/scroll'
import {useClickOutside} from '../hooks'
import {Placement} from '../types'
import {DialogAction} from './types'
Expand Down Expand Up @@ -160,9 +161,9 @@ function PopoverDialogChildren(props: PopoverDialogChildrenProps) {
</div>
)}

<div className={styles.content}>
<ScrollContainer className={styles.content}>
<div className={styles.contentWrapper}>{children}</div>
</div>
</ScrollContainer>

{actions.length > 0 && (
<div className={styles.footer}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import IconMoreVert from 'part:@sanity/base/more-vert-icon'
import {IntentLink} from 'part:@sanity/base/router'
import Button from 'part:@sanity/components/buttons/default'
import IntentButton from 'part:@sanity/components/buttons/intent'
import {ScrollContainer} from 'part:@sanity/components/scroll'
import TabPanel from 'part:@sanity/components/tabs/tab-panel'
import React from 'react'
import {ScrollContainer} from '../../../../components/scroll'
import {childrenToElementArray} from '../helpers'
import {MenuItem, MenuItemGroup} from '../menus/types'
import Styleable from '../utilities/Styleable'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React, {useMemo} from 'react'
import {Path} from '@sanity/types'
import {ScrollContainer} from 'part:@sanity/components/scroll'
import {ScrollContainer} from '../../components/scroll'
import {Tracker, ConnectorContext} from '../'
import {ENABLED} from '../constants'
import {ConnectorsOverlay} from './ConnectorsOverlay'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import {sortBy} from 'lodash'
import {Path} from '@sanity/types'
import {ScrollMonitor} from 'part:@sanity/components/scroll'
import {ScrollMonitor} from '../../components/scroll'
import {useReportedValues, Reported, TrackedChange} from '../'
import {findMostSpecificTarget} from '../helpers/findMostSpecificTarget'
import {getElementGeometry} from '../helpers/getElementGeometry'
Expand Down
1 change: 1 addition & 0 deletions packages/@sanity/base/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './formField'
export * from './scroll'
export * from './transitional'
export * from './UserAvatar'
export * from './zOffsets'
10 changes: 10 additions & 0 deletions packages/@sanity/base/src/components/scroll/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react'
import {ScrollContext} from './scrollContext'
import {Subscriber} from 'nano-pubsub'

export function useOnScroll(callback: Subscriber<Event>) {
const parentContext = React.useContext(ScrollContext)
React.useEffect(() => {
return parentContext?.subscribe(callback)
}, [callback])
}
4 changes: 4 additions & 0 deletions packages/@sanity/base/src/components/scroll/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './hooks'
export * from './scrollContainer'
export * from './scrollMonitor'
export * from './types'
75 changes: 75 additions & 0 deletions packages/@sanity/base/src/components/scroll/scrollContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react'
import createPubSub from 'nano-pubsub'
import {ScrollContext} from './scrollContext'

interface ScrollContainerProps<T extends React.ElementType>
extends Omit<React.HTMLProps<T>, 'as' | 'onScroll'> {
as?: React.ElementType | keyof JSX.IntrinsicElements
onScroll?: (event: Event) => () => void
}

const noop = () => undefined

/**
* This provides a utility function for use within Sanity Studios to create scrollable containers
* It also provides a way for components inside a scrollable container to track onScroll on their first parent scroll container
* Note: this is used by different studio utilities to track positions of elements on screen
* Note: It will call any given `onScroll` callback with a Native DOM Event, and not a React Synthetic event
* Note: It will not make sure the element is actually scrollable, this still needs to be done with css as usual
*/
export const ScrollContainer = React.forwardRef(function ScrollContainer<
T extends React.ElementType = 'div'
>(props: ScrollContainerProps<T>, forwardedRef) {
const {as = 'div', onScroll, ...rest} = props

const selfRef = React.useRef<HTMLElement | null>(null)
const parentContext = React.useContext(ScrollContext)
const childContext = React.useMemo(() => createPubSub<Event>(), [])

const handleScroll = React.useCallback(
(event: Event) => {
childContext.publish(event)
},
[childContext]
)

React.useEffect(() => {
if (onScroll) {
// emit scroll events from children
return childContext.subscribe(onScroll)
}
return noop
}, [childContext, onScroll])

React.useEffect(() => {
// let events bubble up
if (parentContext) {
return childContext.subscribe(parentContext.publish)
}
return noop
}, [parentContext, childContext])

React.useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
selfRef.current!.addEventListener('scroll', handleScroll, {
passive: true,
})

return () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
selfRef.current!.removeEventListener('scroll', handleScroll)
}
}, [handleScroll])

const setRef = (el: HTMLElement | null) => {
selfRef.current = el
if (typeof forwardedRef === 'function') forwardedRef(el)
else if (forwardedRef && typeof forwardedRef === 'object') forwardedRef.current = el
}

return (
<ScrollContext.Provider value={childContext}>
{React.createElement(as, {ref: setRef, ...rest})}
</ScrollContext.Provider>
)
})
4 changes: 4 additions & 0 deletions packages/@sanity/base/src/components/scroll/scrollContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import React from 'react'
import {PubSub} from 'nano-pubsub'

export const ScrollContext = React.createContext<null | PubSub<Event>>(null)
13 changes: 13 additions & 0 deletions packages/@sanity/base/src/components/scroll/scrollMonitor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react'
import {useOnScroll} from './hooks'
import {ScrollEventHandler} from './types'

interface ScrollMonitorProps {
onScroll: ScrollEventHandler
children?: React.ReactNode
}

export function ScrollMonitor({onScroll, children}: ScrollMonitorProps) {
useOnScroll(onScroll)
return <>{children}</>
}
5 changes: 5 additions & 0 deletions packages/@sanity/base/src/components/scroll/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type ScrollEventHandler = (event: Event) => void

export interface ScrollContextValue {
onScroll?: ScrollEventHandler
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import {
ObjectSchemaType,
} from '@sanity/field/diff'
import CloseIcon from 'part:@sanity/base/close-icon'
import {UserAvatar} from '@sanity/base/components'
import {UserAvatar, ScrollContainer} from '@sanity/base/components'
import Button from 'part:@sanity/components/buttons/default'
import {AvatarStack} from 'part:@sanity/components/avatar'
import {ScrollContainer} from 'part:@sanity/components/scroll'
import {TooltipProvider} from 'part:@sanity/components/tooltip'
import React, {useCallback, useRef} from 'react'
import {DropdownButton} from '../../../components/DropdownButton'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {MenuItemGroup} from '@sanity/base/__legacy/@sanity/components'
import {BoundaryElementProvider, Card, Layer, PortalProvider, usePortal} from '@sanity/ui'
import classNames from 'classnames'
import {ScrollContainer} from 'part:@sanity/components/scroll'
import React, {createElement, useCallback, useMemo, useRef, useState} from 'react'
import {Path} from '@sanity/types'
import {LegacyLayerProvider} from '@sanity/base/components'
import {LegacyLayerProvider, ScrollContainer} from '@sanity/base/components'
import {useDeskToolFeatures} from '../../../features'
import {useDocumentHistory} from '../documentHistory'
import {Doc, DocumentView} from '../types'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import {Marker} from '@sanity/types'
import {useLayer} from '@sanity/ui'
import {FOCUS_TERMINATOR} from '@sanity/util/paths'
import {ScrollContainer} from 'part:@sanity/components/scroll'
import {ScrollContainer} from '@sanity/base/components'
import React, {useMemo, useCallback, useEffect, useState} from 'react'
import PatchEvent from '../../PatchEvent'
import styles from './PortableTextInput.css'
Expand Down

0 comments on commit 0a755c9

Please sign in to comment.