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'
|
| 'error'
|
||||||
| 'offer'
|
| 'offer'
|
||||||
|
|
||||||
type NotificationProps = {
|
export type NotificationProps = {
|
||||||
action?: React.ReactElement
|
action?: React.ReactElement
|
||||||
ariaLive?: 'polite' | 'off' | 'assertive'
|
ariaLive?: 'polite' | 'off' | 'assertive'
|
||||||
className?: string
|
className?: string
|
||||||
|
@ -21,6 +21,7 @@ type NotificationProps = {
|
||||||
onDismiss?: () => void
|
onDismiss?: () => void
|
||||||
title?: string
|
title?: string
|
||||||
type: NotificationType
|
type: NotificationType
|
||||||
|
id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function NotificationIcon({
|
function NotificationIcon({
|
||||||
|
@ -57,6 +58,7 @@ function Notification({
|
||||||
onDismiss,
|
onDismiss,
|
||||||
title,
|
title,
|
||||||
type,
|
type,
|
||||||
|
id,
|
||||||
}: NotificationProps) {
|
}: NotificationProps) {
|
||||||
type = type || 'info'
|
type = type || 'info'
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
@ -83,6 +85,7 @@ function Notification({
|
||||||
className={notificationClassName}
|
className={notificationClassName}
|
||||||
aria-live={ariaLive || 'off'}
|
aria-live={ariaLive || 'off'}
|
||||||
role="alert"
|
role="alert"
|
||||||
|
id={id}
|
||||||
>
|
>
|
||||||
<NotificationIcon notificationType={type} customIcon={customIcon} />
|
<NotificationIcon notificationType={type} customIcon={customIcon} />
|
||||||
|
|
||||||
|
|
|
@ -192,15 +192,6 @@ input[type='date'] {
|
||||||
margin-bottom: @form-group-margin-bottom;
|
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
|
// Checkboxes and radios
|
||||||
//
|
//
|
||||||
// Indent the labels to position radios/checkboxes as hanging controls.
|
// Indent the labels to position radios/checkboxes as hanging controls.
|
||||||
|
|
|
@ -180,6 +180,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification-with-scroll-margin {
|
||||||
|
scroll-margin: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.notification-list {
|
.notification-list {
|
||||||
.notification {
|
.notification {
|
||||||
margin-bottom: @margin-md;
|
margin-bottom: @margin-md;
|
||||||
|
|
Loading…
Reference in a new issue