mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #16769 from overleaf/jel-scroll-to-notification
[web] Add scroll to notification component GitOrigin-RevId: 096f9f42344729464e7fb38e4f6542cb2e891918
This commit is contained in:
parent
71ca6f05b7
commit
f79f534d9f
4 changed files with 54 additions and 10 deletions
|
@ -0,0 +1,46 @@
|
|||
import Notification, {
|
||||
NotificationProps,
|
||||
} from '@/shared/components/notification'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
function elementIsInView(el: HTMLElement) {
|
||||
const scroll = window.scrollY
|
||||
const boundsTop = el.getBoundingClientRect().top + scroll
|
||||
|
||||
const viewport = {
|
||||
top: scroll,
|
||||
bottom: scroll + window.innerHeight,
|
||||
}
|
||||
|
||||
const bounds = {
|
||||
top: boundsTop,
|
||||
bottom: boundsTop + el.clientHeight,
|
||||
}
|
||||
|
||||
return (
|
||||
(bounds.bottom >= viewport.top && bounds.bottom <= viewport.bottom) ||
|
||||
(bounds.top <= viewport.bottom && bounds.top >= viewport.top)
|
||||
)
|
||||
}
|
||||
|
||||
function NotificationScrolledTo({ ...props }: NotificationProps) {
|
||||
useEffect(() => {
|
||||
if (props.id) {
|
||||
const alert = document.getElementById(props.id)
|
||||
if (alert && !elementIsInView(alert)) {
|
||||
alert.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
}, [props])
|
||||
|
||||
const notificationProps = { ...props }
|
||||
|
||||
if (!notificationProps.className) {
|
||||
notificationProps.className = ''
|
||||
}
|
||||
|
||||
notificationProps.className = `${notificationProps.className} notification-with-scroll-margin`
|
||||
|
||||
return <Notification {...notificationProps} />
|
||||
}
|
||||
export default NotificationScrolledTo
|
|
@ -10,7 +10,7 @@ export type NotificationType =
|
|||
| 'error'
|
||||
| 'offer'
|
||||
|
||||
type NotificationProps = {
|
||||
export type NotificationProps = {
|
||||
action?: React.ReactElement
|
||||
ariaLive?: 'polite' | 'off' | 'assertive'
|
||||
className?: string
|
||||
|
@ -21,6 +21,7 @@ type NotificationProps = {
|
|||
onDismiss?: () => void
|
||||
title?: string
|
||||
type: NotificationType
|
||||
id?: string
|
||||
}
|
||||
|
||||
function NotificationIcon({
|
||||
|
@ -57,6 +58,7 @@ function Notification({
|
|||
onDismiss,
|
||||
title,
|
||||
type,
|
||||
id,
|
||||
}: NotificationProps) {
|
||||
type = type || 'info'
|
||||
const { t } = useTranslation()
|
||||
|
@ -83,6 +85,7 @@ function Notification({
|
|||
className={notificationClassName}
|
||||
aria-live={ariaLive || 'off'}
|
||||
role="alert"
|
||||
id={id}
|
||||
>
|
||||
<NotificationIcon notificationType={type} customIcon={customIcon} />
|
||||
|
||||
|
|
|
@ -192,15 +192,6 @@ input[type='date'] {
|
|||
margin-bottom: @form-group-margin-bottom;
|
||||
}
|
||||
|
||||
// Form alert
|
||||
//
|
||||
// adds top padding/mergin so that element has spacing when scrolled into
|
||||
// view, but no vertical spacing is applied to layout
|
||||
.form-group#form-alert {
|
||||
padding-top: @form-group-margin-bottom;
|
||||
margin-top: -@form-group-margin-bottom;
|
||||
}
|
||||
|
||||
// Checkboxes and radios
|
||||
//
|
||||
// Indent the labels to position radios/checkboxes as hanging controls.
|
||||
|
|
|
@ -180,6 +180,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.notification-with-scroll-margin {
|
||||
scroll-margin: 16px;
|
||||
}
|
||||
|
||||
.notification-list {
|
||||
.notification {
|
||||
margin-bottom: @margin-md;
|
||||
|
|
Loading…
Reference in a new issue