modal-fix, media-query-fix

This commit is contained in:
Untone 2024-06-25 22:10:00 +03:00
parent 1d38c12509
commit c6ae893403
9 changed files with 54 additions and 73 deletions

View File

@ -10,10 +10,15 @@ import { email, setEmail } from './sharedLogic'
import { useSearchParams } from '@solidjs/router' import { useSearchParams } from '@solidjs/router'
import styles from './AuthModal.module.scss' import styles from './AuthModal.module.scss'
export type ConfirmEmailSearchParams = {
access_token?: string
token?: string
}
export const EmailConfirm = () => { export const EmailConfirm = () => {
const { t } = useLocalize() const { t } = useLocalize()
const { hideModal } = useUI() const { hideModal } = useUI()
const [, changeSearchParams] = useSearchParams() const [, changeSearchParams] = useSearchParams<ConfirmEmailSearchParams>()
const { session, authError } = useSession() const { session, authError } = useSession()
const [emailConfirmed, setEmailConfirmed] = createSignal(false) const [emailConfirmed, setEmailConfirmed] = createSignal(false)

View File

@ -2,8 +2,7 @@ import { clsx } from 'clsx'
import { Component, Show, createEffect, createMemo } from 'solid-js' import { Component, Show, createEffect, createMemo } from 'solid-js'
import { Dynamic } from 'solid-js/web' import { Dynamic } from 'solid-js/web'
import { useUI } from '~/context/ui' import { AuthModalSource, useUI } from '~/context/ui'
import type { AuthModalMode } from '~/context/ui'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
import { isMobile } from '../../../utils/media-query' import { isMobile } from '../../../utils/media-query'
import { ChangePasswordForm } from './ChangePasswordForm' import { ChangePasswordForm } from './ChangePasswordForm'
@ -15,11 +14,24 @@ import { SendResetLinkForm } from './SendResetLinkForm'
import { useSearchParams } from '@solidjs/router' import { useSearchParams } from '@solidjs/router'
import styles from './AuthModal.module.scss' import styles from './AuthModal.module.scss'
import { AuthModalSearchParams } from './types'
export type AuthModalMode =
| 'login'
| 'register'
| 'confirm-email'
| 'send-confirm-email'
| 'send-reset-link'
| 'change-password'
export type AuthModalSearchParams = {
mode: AuthModalMode
source?: AuthModalSource
token?: string
}
const AUTH_MODAL_MODES: Record<AuthModalMode, Component> = { const AUTH_MODAL_MODES: Record<AuthModalMode, Component> = {
login: LoginForm, 'login': LoginForm,
register: RegisterForm, 'register': RegisterForm,
'send-reset-link': SendResetLinkForm, 'send-reset-link': SendResetLinkForm,
'confirm-email': EmailConfirm, 'confirm-email': EmailConfirm,
'send-confirm-email': SendEmailConfirm, 'send-confirm-email': SendEmailConfirm,

View File

@ -1,17 +0,0 @@
import { AuthModalMode, AuthModalSource } from '~/context/ui'
export type AuthModalSearchParams = {
mode: AuthModalMode
source?: AuthModalSource
token?: string
}
export type ConfirmEmailSearchParams = {
access_token?: string
token?: string
}
export type CreateChatSearchParams = {
id: number
}
export type { AuthModalSource }

View File

@ -155,8 +155,7 @@ export const Header = (props: Props) => {
const loc = useLocation() const loc = useLocation()
const handleToggleMenuByLink = (event: MouseEvent, route: string) => { const handleToggleMenuByLink = (event: MouseEvent, route: string) => {
event.preventDefault() event.preventDefault()
console.debug(route) console.debug(loc.pathname, route)
console.debug(loc.pathname)
if (!fixed()) return if (!fixed()) return
if (loc.pathname.startsWith(route) || loc.pathname.startsWith(`/${route}`)) { if (loc.pathname.startsWith(route) || loc.pathname.startsWith(`/${route}`)) {
toggleFixed() toggleFixed()
@ -176,7 +175,7 @@ export const Header = (props: Props) => {
<Modal <Modal
variant={searchParams?.source ? 'narrow' : 'wide'} variant={searchParams?.source ? 'narrow' : 'wide'}
name="auth" name="auth"
allowClose={searchParams?.source !== 'authguard'} hideClose={Boolean(searchParams?.source === 'authguard')}
noPadding={true} noPadding={true}
> >
<AuthModal /> <AuthModal />

View File

@ -1,12 +1,10 @@
import { clsx } from 'clsx' import { clsx } from 'clsx'
import type { JSX } from 'solid-js' import type { JSX } from 'solid-js'
import { Show, createEffect, createMemo, createSignal } from 'solid-js' import { Show } from 'solid-js'
import { useUI } from '~/context/ui' import { useUI } from '~/context/ui'
import { isPortrait } from '~/utils/media-query'
import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler' import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler'
import { Icon } from '../../_shared/Icon' import { Icon } from '../../_shared/Icon'
import { useNavigate } from '@solidjs/router'
import { mediaMatches } from '~/utils/media-query'
import styles from './Modal.module.scss' import styles from './Modal.module.scss'
interface Props { interface Props {
@ -16,44 +14,25 @@ interface Props {
onClose?: () => void onClose?: () => void
noPadding?: boolean noPadding?: boolean
maxHeight?: boolean maxHeight?: boolean
allowClose?: boolean hideClose?: boolean
isResponsive?: boolean isResponsive?: boolean
} }
export const Modal = (props: Props) => { export const Modal = (props: Props) => {
const { modal, hideModal } = useUI() const { modal, hideModal } = useUI()
const [visible, setVisible] = createSignal(false)
const allowClose = createMemo(() => props.allowClose !== false)
const [isMobileView, setIsMobileView] = createSignal(false)
const navigate = useNavigate()
const handleHide = () => { const handleHide = () => {
if (modal()) { console.debug('[Modal.handleHide]', modal())
if (allowClose()) { modal() && props.onClose?.()
props.onClose?.()
} else {
navigate('/')
}
}
hideModal() hideModal()
} }
useEscKeyDownHandler(handleHide) useEscKeyDownHandler(handleHide)
createEffect(() => {
setVisible(modal() === props.name)
})
createEffect(() => {
if (props.isResponsive) {
setIsMobileView(!mediaMatches.sm)
}
})
return ( return (
<Show when={visible()}> <Show when={modal() === props.name}>
<div <div
class={clsx(styles.backdrop, [styles[`modal-${props.name}` as keyof typeof styles]], { class={clsx(styles.backdrop, [styles[`modal-${props.name}` as keyof typeof styles]], {
[styles.isMobile]: isMobileView(), [styles.isMobile]: props.isResponsive && isPortrait(),
})} })}
onClick={handleHide} onClick={handleHide}
> >
@ -68,7 +47,7 @@ export const Modal = (props: Props) => {
onClick={(event) => event.stopPropagation()} onClick={(event) => event.stopPropagation()}
> >
<div class={styles.modalInner}>{props.children}</div> <div class={styles.modalInner}>{props.children}</div>
<Show when={!isMobileView()}> <Show when={!isPortrait()}>
<div class={styles.close} onClick={handleHide}> <div class={styles.close} onClick={handleHide}>
<Icon name="close" class={styles.icon} /> <Icon name="close" class={styles.icon} />
</div> </div>

View File

@ -14,7 +14,7 @@ export const ShareModal = (props: Props) => {
const { t } = useLocalize() const { t } = useLocalize()
const { hideModal } = useUI() const { hideModal } = useUI()
return ( return (
<Modal name="share" variant="medium" allowClose={true}> <Modal name="share" variant="medium">
<h2>{t('Share publication')}</h2> <h2>{t('Share publication')}</h2>
<ShareLinks <ShareLinks
variant="inModal" variant="inModal"

View File

@ -23,6 +23,10 @@ type InboxContextType = {
sendMessage?: (args: MutationCreate_MessageArgs) => void sendMessage?: (args: MutationCreate_MessageArgs) => void
} }
export type CreateChatSearchParams = {
inbox: number
}
const InboxContext = createContext<InboxContextType>({} as InboxContextType) const InboxContext = createContext<InboxContextType>({} as InboxContextType)
export function useInbox() { export function useInbox() {

View File

@ -70,14 +70,6 @@ export const SnackbarProvider = (props: { children: JSX.Element }) => {
return <SnackbarContext.Provider value={value}>{props.children}</SnackbarContext.Provider> return <SnackbarContext.Provider value={value}>{props.children}</SnackbarContext.Provider>
} }
export type AuthModalMode =
| 'login'
| 'register'
| 'confirm-email'
| 'send-confirm-email'
| 'send-reset-link'
| 'change-password'
export type AuthModalSource = export type AuthModalSource =
| 'discussions' | 'discussions'
| 'vote' | 'vote'
@ -135,7 +127,7 @@ type ConfirmMessage = {
} }
type UIContextType = { type UIContextType = {
modal: Accessor<string> modal: Accessor<ModalType|null>
showModal: (m: ModalType, source?: AuthModalSource) => void showModal: (m: ModalType, source?: AuthModalSource) => void
hideModal: () => void hideModal: () => void
confirmMessage: Accessor<ConfirmMessage> confirmMessage: Accessor<ConfirmMessage>
@ -151,7 +143,7 @@ export function useUI() {
export const UIProvider = (props: { children: JSX.Element }) => { export const UIProvider = (props: { children: JSX.Element }) => {
const [, setSearchParams] = useSearchParams<Record<string, string>>() const [, setSearchParams] = useSearchParams<Record<string, string>>()
const [modal, setModal] = createSignal<ModalType>('') const [modal, setModal] = createSignal<ModalType|null>(null)
const [confirmMessage, setConfirmMessage] = createSignal<ConfirmMessage>({} as ConfirmMessage) const [confirmMessage, setConfirmMessage] = createSignal<ConfirmMessage>({} as ConfirmMessage)
let resolveFn: (value: boolean) => void let resolveFn: (value: boolean) => void
@ -172,6 +164,7 @@ export const UIProvider = (props: { children: JSX.Element }) => {
} }
const showModal = (modalType: ModalType, modalSource?: AuthModalSource) => { const showModal = (modalType: ModalType, modalSource?: AuthModalSource) => {
console.log('[context.ui] showModal()', modalType)
if (modalSource) { if (modalSource) {
setSearchParams({ source: modalSource }) setSearchParams({ source: modalSource })
} }
@ -179,8 +172,9 @@ export const UIProvider = (props: { children: JSX.Element }) => {
} }
const hideModal = () => { const hideModal = () => {
setSearchParams({}, { replace: true }) console.log('[context.ui] hideModal()', modal())
setModal('') setTimeout(() => setModal(null), 1) // NOTE: modal rerender fix
setSearchParams({source: undefined, m: undefined, mode: undefined})
} }
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
@ -190,12 +184,14 @@ export const UIProvider = (props: { children: JSX.Element }) => {
[modal, () => searchParams?.m || ''], [modal, () => searchParams?.m || ''],
([m1, m2]) => { ([m1, m2]) => {
const m = m1 || m2 || '' const m = m1 || m2 || ''
setModal((_) => m as ModalType) console.log('[context.ui] search params change', m)
if (m) { if (m) {
showModal(m as ModalType) showModal(m as ModalType)
} else {
setModal(null)
} }
}, },
{ defer: true }, {},
), ),
) )

View File

@ -8,6 +8,9 @@ export const breakpoints = {
xl: '1200px', xl: '1200px',
xxl: '1400px', xxl: '1400px',
} }
export const isMobile = createMediaQuery('(max-width: 767px)') export const isPortrait = createMediaQuery(`(max-width: ${breakpoints.sm})`)
export const isDesktop = createMediaQuery('(min-width: 1200px)') export const isMobile = createMediaQuery(`(max-width: ${breakpoints.md})`)
export const isTablet = createMediaQuery(`(min-width: ${breakpoints.sm}, max-width: ${breakpoints.lg})`)
export const isDesktop = createMediaQuery(`(min-width: ${breakpoints.md}, max-width: ${breakpoints.xl})`)
export const isBig = createMediaQuery(`(min-width: ${breakpoints.xl})`)
export const mediaMatches = createBreakpoints(breakpoints) export const mediaMatches = createBreakpoints(breakpoints)