diff --git a/components/party/EditPartyModal/index.module.scss b/components/party/EditPartyModal/index.module.scss index 8c8752e1..229e15ba 100644 --- a/components/party/EditPartyModal/index.module.scss +++ b/components/party/EditPartyModal/index.module.scss @@ -1,56 +1,39 @@ -.EditTeam.DialogContent { - min-height: 80vh; +.segmentedControlWrapper { + display: flex; + align-items: center; + justify-content: center; + padding: $unit 0; +} - .Container.Scrollable { - height: 100%; - display: flex; - flex-direction: column; - flex-grow: 1; - } +.content { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: $unit-2x; + height: 100%; + overflow: hidden; - .Content { - display: flex; - flex-direction: column; - flex-grow: 1; - gap: $unit-2x; - } - - .Fields { + .fields { display: flex; flex-direction: column; flex-grow: 1; gap: $unit; - } + padding: 0 $unit-4x; + overflow: hidden; - .ExtraNotice { - background: var(--extra-purple-bg); - border-radius: $input-corner; - color: var(--extra-purple-text); - font-weight: $medium; - padding: $unit-2x; - } - - .DescriptionField { - display: flex; - flex-direction: column; - justify-content: inherit; - gap: $unit; - flex-grow: 1; - - .Left { - flex-grow: 0; - } - - textarea.Input { - flex-grow: 1; - - &::placeholder { - color: var(--text-secondary); - } - } - - .Image { - display: none; + &.scrollable { + overflow-y: auto; } } } + +.extraNotice { + background: var(--extra-purple-bg); + border-radius: $input-corner; + font-weight: $medium; + padding: $unit-2x; + + p { + color: var(--extra-purple-dark-text); + } +} diff --git a/components/party/EditPartyModal/index.tsx b/components/party/EditPartyModal/index.tsx index 265a767b..9f5064a4 100644 --- a/components/party/EditPartyModal/index.tsx +++ b/components/party/EditPartyModal/index.tsx @@ -2,16 +2,14 @@ import React, { useEffect, useRef, useState } from 'react' import { useRouter } from 'next/router' import { useSnapshot } from 'valtio' import { useTranslation } from 'react-i18next' +import classNames from 'classnames' +import debounce from 'lodash.debounce' -import { - Dialog, - DialogTrigger, - DialogClose, - DialogTitle, -} from '~components/common/Dialog' +import { Dialog, DialogTrigger } from '~components/common/Dialog' +import DialogHeader from '~components/common/DialogHeader' +import DialogFooter from '~components/common/DialogFooter' import DialogContent from '~components/common/DialogContent' import Button from '~components/common/Button' -import CharLimitedFieldset from '~components/common/CharLimitedFieldset' import DurationInput from '~components/common/DurationInput' import InputTableField from '~components/common/InputTableField' import RaidCombobox from '~components/raids/RaidCombobox' @@ -19,6 +17,7 @@ import SegmentedControl from '~components/common/SegmentedControl' import Segment from '~components/common/Segment' import SwitchTableField from '~components/common/SwitchTableField' import TableField from '~components/common/TableField' +import Textarea from '~components/common/Textarea' import type { DetailsObject } from 'types' import type { DialogProps } from '@radix-ui/react-dialog' @@ -26,8 +25,8 @@ import type { DialogProps } from '@radix-ui/react-dialog' import { appState } from '~utils/appState' import CheckIcon from '~public/icons/Check.svg' -import CrossIcon from '~public/icons/Cross.svg' import styles from './index.module.scss' +import Input from '~components/common/Input' interface Props extends DialogProps { party?: Party @@ -46,8 +45,9 @@ const EditPartyModal = ({ updateCallback, ...props }: Props) => { // Refs const headerRef = React.createRef() + const topContainerRef = React.createRef() const footerRef = React.createRef() - const descriptionInput = useRef(null) + const descriptionInput = useRef(null) // States: Component const [open, setOpen] = useState(false) @@ -72,6 +72,12 @@ const EditPartyModal = ({ updateCallback, ...props }: Props) => { const [turnCount, setTurnCount] = useState(undefined) const [clearTime, setClearTime] = useState(0) + // Classes + const fieldsClasses = classNames({ + [styles.fields]: true, + [styles.scrollable]: currentSegment === 1, + }) + // Hooks useEffect(() => { persistFromState() @@ -152,14 +158,9 @@ const EditPartyModal = ({ updateCallback, ...props }: Props) => { if (!isNaN(numericalValue)) setChainCount(numericalValue) } - function handleTextAreaChanged( - event: React.ChangeEvent - ) { + function handleTextAreaChanged(event: React.ChangeEvent) { event.preventDefault() - - const { name, value } = event.target let newErrors = errors - setErrors(newErrors) } @@ -172,6 +173,101 @@ const EditPartyModal = ({ updateCallback, ...props }: Props) => { } } + // Handlers + function handleScroll(event: React.UIEvent) { + const scrollTop = event.currentTarget.scrollTop + const scrollHeight = event.currentTarget.scrollHeight + const clientHeight = event.currentTarget.clientHeight + + if (topContainerRef && topContainerRef.current) + manipulateHeaderShadow(topContainerRef.current, scrollTop) + + if (footerRef && footerRef.current) + manipulateFooterShadow( + footerRef.current, + scrollTop, + scrollHeight, + clientHeight + ) + } + + function manipulateHeaderShadow(header: HTMLDivElement, scrollTop: number) { + const boxShadowBase = '0 2px 8px' + const maxValue = 50 + + if (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) + + header.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, ${boxShadowOpacity})` + header.style.borderBottomColor = `rgba(0, 0, 0, ${borderOpacity})` + } + } + + function manipulateFooterShadow( + footer: HTMLDivElement, + scrollTop: number, + scrollHeight: number, + clientHeight: number + ) { + const boxShadowBase = '0 -2px 8px' + const minValue = scrollHeight - 200 + const currentScroll = scrollTop + clientHeight + + if (currentScroll >= minValue) { + const input = currentScroll < minValue ? minValue : currentScroll + + const boxShadowOpacity = mapRange( + input, + minValue, + scrollHeight, + 0.16, + 0.0 + ) + const borderOpacity = mapRange(input, minValue, scrollHeight, 0.24, 0.0) + + footer.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, ${boxShadowOpacity})` + footer.style.borderTopColor = `rgba(0, 0, 0, ${borderOpacity})` + } + } + + const calculateFooterShadow = debounce(() => { + const boxShadowBase = '0 -2px 8px' + const scrollable = document.querySelector(`.${styles.scrollValue}`) + const footer = footerRef + + if (footer && footer.current) { + if (scrollable && scrollable.clientHeight >= scrollable.scrollHeight) { + footer.current.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, 0)` + footer.current.style.borderTopColor = `rgba(0, 0, 0, 0)` + } else { + footer.current.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, 0.16)` + footer.current.style.borderTopColor = `rgba(0, 0, 0, 0.24)` + } + } + }, 100) + + useEffect(() => { + window.addEventListener('resize', calculateFooterShadow) + calculateFooterShadow() + + return () => { + window.removeEventListener('resize', calculateFooterShadow) + } + }, [calculateFooterShadow]) + + function mapRange( + value: number, + low1: number, + high1: number, + low2: number, + high2: number + ) { + return low2 + ((high2 - low2) * (value - low1)) / (high1 - low1) + } + // Methods: Data methods function persistFromState() { if (!party) return @@ -189,7 +285,7 @@ const EditPartyModal = ({ updateCallback, ...props }: Props) => { } function updateDetails(event: React.MouseEvent) { - const descriptionValue = descriptionInput.current?.value + const descriptionValue = descriptionInput.current?.innerHTML const details: DetailsObject = { fullAuto: fullAuto, autoGuard: autoGuard, @@ -210,8 +306,8 @@ const EditPartyModal = ({ updateCallback, ...props }: Props) => { } // Methods: Rendering methods - const segmentedControl = () => { - return ( + const segmentedControl = ( + + ) - const nameField = () => { - return ( - - ) - } + const nameField = ( + + ) - const raidField = () => { - return ( - - ) - } + const raidField = ( + + ) const extraNotice = () => { if (extra) { return ( -
- +
+

{raid && raid.group.guidebooks ? t('modals.edit_team.extra_notice_guidebooks') : t('modals.edit_team.extra_notice')} - +

) } } - const descriptionField = () => { - return ( -
-