Implement shadow on DialogHeader in static modals
This commit is contained in:
parent
16ed034b16
commit
baeaf4f73d
8 changed files with 108 additions and 35 deletions
|
|
@ -29,19 +29,10 @@ const AboutModal = () => {
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
className="About"
|
className="About"
|
||||||
|
title={t('menu.about')}
|
||||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||||
onEscapeKeyDown={() => {}}
|
onEscapeKeyDown={() => {}}
|
||||||
>
|
>
|
||||||
<div className="DialogHeader">
|
|
||||||
<DialogTitle className="DialogTitle">{t('menu.about')}</DialogTitle>
|
|
||||||
<DialogClose className="DialogClose" asChild>
|
|
||||||
<span>
|
|
||||||
<CrossIcon />
|
|
||||||
</span>
|
|
||||||
</DialogClose>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div className="sections">
|
<div className="sections">
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
.Changelog.DialogContent {
|
.Changelog.DialogContent {
|
||||||
gap: 0;
|
gap: 0;
|
||||||
padding-bottom: $unit-4x;
|
|
||||||
|
|
||||||
& > div:not(.DialogHeader) {
|
& > div:not(.DialogHeader) {
|
||||||
padding: 0 $unit-4x;
|
padding: 0 $unit-4x;
|
||||||
|
|
@ -30,6 +29,11 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 1fr auto;
|
grid-template-rows: 1fr auto;
|
||||||
gap: $unit;
|
gap: $unit;
|
||||||
|
|
||||||
|
& > h4 {
|
||||||
|
font-weight: $medium;
|
||||||
|
font-size: $font-regular;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.items {
|
.items {
|
||||||
|
|
@ -66,6 +70,7 @@
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin-bottom: $unit-half;
|
margin-bottom: $unit-half;
|
||||||
|
font-size: $font-regular;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,20 +26,10 @@ const ChangelogModal = () => {
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
className="Changelog"
|
className="Changelog"
|
||||||
|
title={t('menu.changelog')}
|
||||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||||
onEscapeKeyDown={() => {}}
|
onEscapeKeyDown={() => {}}
|
||||||
>
|
>
|
||||||
<div className="DialogHeader">
|
|
||||||
<DialogTitle className="DialogTitle">
|
|
||||||
{t('menu.changelog')}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogClose className="DialogClose" asChild>
|
|
||||||
<span>
|
|
||||||
<CrossIcon />
|
|
||||||
</span>
|
|
||||||
</DialogClose>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="updates">
|
<div className="updates">
|
||||||
<section className="version" data-version="1.0">
|
<section className="version" data-version="1.0">
|
||||||
<div className="top">
|
<div className="top">
|
||||||
|
|
|
||||||
|
|
@ -81,17 +81,45 @@ const CharacterModal = ({
|
||||||
|
|
||||||
// UI state
|
// UI state
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
const [scrolled, setScrolled] = useState(false)
|
||||||
const [formValid, setFormValid] = useState(false)
|
const [formValid, setFormValid] = useState(false)
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
const headerClasses = classNames({
|
const headerClasses = classNames({
|
||||||
DialogHeader: true,
|
DialogHeader: true,
|
||||||
|
Short: true,
|
||||||
Scrolled: scrolled,
|
Scrolled: scrolled,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Callbacks and Hooks
|
||||||
|
const onScroll = useCallback((event: Event) => {
|
||||||
|
// const dialogContent = event.target as HTMLDivElement
|
||||||
|
// const { scrollTop } = dialogContent
|
||||||
|
// if (scrollTop > 150) {
|
||||||
|
// console.log(scrollTop, scrollTop % 5)
|
||||||
|
// if (scrollTop > 20) setScrolled(true)
|
||||||
|
// else setScrolled(false)
|
||||||
|
// console.log('scrollTop', scrollTop)
|
||||||
|
// }
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOpen(modalOpen)
|
setOpen(modalOpen)
|
||||||
}, [modalOpen])
|
}, [modalOpen])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//add eventlistener to window
|
||||||
|
// const dialogContent = document.querySelector('.DialogContent')
|
||||||
|
// if (dialogContent) {
|
||||||
|
// dialogContent.addEventListener('scroll', onScroll, { passive: true })
|
||||||
|
// // remove event on unmount to prevent a memory leak with the cleanup
|
||||||
|
// return () => {
|
||||||
|
// // what does passive do?
|
||||||
|
// dialogContent.removeEventListener('scroll', onScroll)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}, [])
|
||||||
|
|
||||||
// Character properties: Perpetuity
|
// Character properties: Perpetuity
|
||||||
const [perpetuity, setPerpetuity] = useState(false)
|
const [perpetuity, setPerpetuity] = useState(false)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,14 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Scrollable {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.DialogHeader {
|
.DialogHeader {
|
||||||
background: var(--dialog-bg);
|
background: var(--dialog-bg);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0);
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: $unit-2x;
|
gap: $unit-2x;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
import './index.scss'
|
import { DialogClose, DialogTitle } from '~components/Dialog'
|
||||||
import Overlay from '~components/Overlay'
|
import Overlay from '~components/Overlay'
|
||||||
|
|
||||||
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
interface Props
|
interface Props
|
||||||
extends React.DetailedHTMLProps<
|
extends React.DetailedHTMLProps<
|
||||||
React.DialogHTMLAttributes<HTMLDivElement>,
|
React.DialogHTMLAttributes<HTMLDivElement>,
|
||||||
HTMLDivElement
|
HTMLDivElement
|
||||||
> {
|
> {
|
||||||
|
header?: React.ReactNode
|
||||||
|
title?: string
|
||||||
onEscapeKeyDown: (event: KeyboardEvent) => void
|
onEscapeKeyDown: (event: KeyboardEvent) => void
|
||||||
onOpenAutoFocus: (event: Event) => void
|
onOpenAutoFocus: (event: Event) => void
|
||||||
}
|
}
|
||||||
|
|
@ -18,10 +23,60 @@ const DialogContent = React.forwardRef<HTMLDivElement, Props>(function dialog(
|
||||||
{ children, ...props },
|
{ children, ...props },
|
||||||
forwardedRef
|
forwardedRef
|
||||||
) {
|
) {
|
||||||
|
// Classes
|
||||||
const classes = classNames(props.className, {
|
const classes = classNames(props.className, {
|
||||||
DialogContent: true,
|
DialogContent: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Refs
|
||||||
|
const headerRef = React.createRef<HTMLDivElement>()
|
||||||
|
const containerRef = React.createRef<HTMLDivElement>()
|
||||||
|
|
||||||
|
// Elements
|
||||||
|
const genericHeader = (
|
||||||
|
<div className="DialogHeader" ref={headerRef}>
|
||||||
|
<DialogTitle className="DialogTitle">
|
||||||
|
{props.title ? props.title : ''}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogClose className="DialogClose" asChild>
|
||||||
|
<span>
|
||||||
|
<CrossIcon />
|
||||||
|
</span>
|
||||||
|
</DialogClose>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
function handleScroll(event: React.UIEvent<HTMLDivElement, UIEvent>) {
|
||||||
|
const headerElement = headerRef.current
|
||||||
|
const scrollTop = event.currentTarget?.scrollTop
|
||||||
|
const boxShadowBase = '0 2px 8px'
|
||||||
|
const maxValue = 50
|
||||||
|
|
||||||
|
if (headerElement && scrollTop >= 0) {
|
||||||
|
const input = scrollTop > maxValue ? maxValue : scrollTop
|
||||||
|
|
||||||
|
const boxShadowOpacity = mapRange(input, 0, maxValue, 0.0, 0.16)
|
||||||
|
const borderOpacity = mapRange(input, 0, maxValue, 0.0, 0.24)
|
||||||
|
console.log(
|
||||||
|
`Scroll top: ${scrollTop}, interpolated opacity: ${boxShadowOpacity}`
|
||||||
|
)
|
||||||
|
|
||||||
|
headerElement.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, ${boxShadowOpacity})`
|
||||||
|
headerElement.style.borderBottomColor = `rgba(0, 0, 0, ${borderOpacity})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapRange(
|
||||||
|
value: number,
|
||||||
|
low1: number,
|
||||||
|
high1: number,
|
||||||
|
low2: number,
|
||||||
|
high2: number
|
||||||
|
) {
|
||||||
|
return low2 + ((high2 - low2) * (value - low1)) / (high1 - low1)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogPrimitive.Portal>
|
<DialogPrimitive.Portal>
|
||||||
<dialog className="Dialog">
|
<dialog className="Dialog">
|
||||||
|
|
@ -31,8 +86,15 @@ const DialogContent = React.forwardRef<HTMLDivElement, Props>(function dialog(
|
||||||
onOpenAutoFocus={props.onOpenAutoFocus}
|
onOpenAutoFocus={props.onOpenAutoFocus}
|
||||||
onEscapeKeyDown={props.onEscapeKeyDown}
|
onEscapeKeyDown={props.onEscapeKeyDown}
|
||||||
ref={forwardedRef}
|
ref={forwardedRef}
|
||||||
|
>
|
||||||
|
{props.title ? genericHeader : ''}
|
||||||
|
<div
|
||||||
|
className="Scrollable"
|
||||||
|
ref={containerRef}
|
||||||
|
onScroll={handleScroll}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
</div>
|
||||||
</DialogPrimitive.Content>
|
</DialogPrimitive.Content>
|
||||||
</dialog>
|
</dialog>
|
||||||
<Overlay visible={true} open={true} />
|
<Overlay visible={true} open={true} />
|
||||||
|
|
|
||||||
|
|
@ -27,18 +27,10 @@ const RoadmapModal = () => {
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
className="Roadmap"
|
className="Roadmap"
|
||||||
|
title={t('title')}
|
||||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||||
onEscapeKeyDown={() => {}}
|
onEscapeKeyDown={() => {}}
|
||||||
>
|
>
|
||||||
<div className="DialogHeader">
|
|
||||||
<DialogTitle className="DialogTitle">{t('title')}</DialogTitle>
|
|
||||||
<DialogClose className="DialogClose" asChild>
|
|
||||||
<span>
|
|
||||||
<CrossIcon />
|
|
||||||
</span>
|
|
||||||
</DialogClose>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<section className="notes">
|
<section className="notes">
|
||||||
<p>{t('blurb')}</p>
|
<p>{t('blurb')}</p>
|
||||||
|
|
|
||||||
|
|
@ -346,7 +346,6 @@ const WeaponModal = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// TODO: Refactor into Dialog component
|
|
||||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||||
<DialogTrigger asChild>{children}</DialogTrigger>
|
<DialogTrigger asChild>{children}</DialogTrigger>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue