1
0
Fork 0
forked from Mirrors/gomuks

web/modal: add generic modal component

This commit is contained in:
Tulir Asokan 2024-10-25 16:35:06 +03:00
parent 692cb323a5
commit d18b7a43a1
8 changed files with 71 additions and 16 deletions

View file

@ -18,9 +18,10 @@ import { ScaleLoader } from "react-spinners"
import Client from "./api/client.ts"
import WSClient from "./api/wsclient.ts"
import { ClientContext } from "./ui/ClientContext.ts"
import { LightboxWrapper } from "./ui/Lightbox.tsx"
import MainScreen from "./ui/MainScreen.tsx"
import { LoginScreen, VerificationScreen } from "./ui/login"
import { LightboxWrapper } from "./ui/modal/Lightbox.tsx"
import { ModalWrapper } from "./ui/modal/Modal.tsx"
import { useEventAsState } from "./util/eventdispatcher.ts"
function App() {
@ -53,7 +54,9 @@ function App() {
} else {
return <ClientContext value={client}>
<LightboxWrapper>
<ModalWrapper>
<MainScreen/>
</ModalWrapper>
</LightboxWrapper>
</ClientContext>
}

View file

@ -17,8 +17,8 @@ import { use, useRef } from "react"
import { getAvatarURL } from "@/api/media.ts"
import { RoomStateStore } from "@/api/statestore"
import { useNonNullEventAsState } from "@/util/eventdispatcher.ts"
import { LightboxContext } from "./Lightbox.tsx"
import MessageComposer from "./composer/MessageComposer.tsx"
import { LightboxContext } from "./modal/Lightbox.tsx"
import { RoomContext, RoomContextData } from "./roomcontext.ts"
import TimelineView from "./timeline/TimelineView.tsx"
import BackIcon from "@/icons/back.svg?react"

View file

@ -1,12 +1,17 @@
div.overlay {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.75);
display: flex;
align-items: center;
justify-content: center;
&.lightbox > div.controls {
&.dimmed {
background-color: rgba(0, 0, 0, 0.75);
}
}
div.lightbox {
> div.controls {
position: fixed;
top: .5rem;
right: .5rem;
@ -29,7 +34,7 @@ div.overlay {
}
}
&.lightbox > img {
> img {
max-width: 75%;
max-height: 75%;

View file

@ -14,12 +14,12 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import React, { Component, RefObject, createContext, createRef, useCallback, useState } from "react"
import CloseIcon from "../icons/close.svg?react"
import DownloadIcon from "../icons/download.svg?react"
import RotateLeftIcon from "../icons/rotate-left.svg?react"
import RotateRightIcon from "../icons/rotate-right.svg?react"
import ZoomInIcon from "../icons/zoom-in.svg?react"
import ZoomOutIcon from "../icons/zoom-out.svg?react"
import CloseIcon from "@/icons/close.svg?react"
import DownloadIcon from "@/icons/download.svg?react"
import RotateLeftIcon from "@/icons/rotate-left.svg?react"
import RotateRightIcon from "@/icons/rotate-right.svg?react"
import ZoomInIcon from "@/icons/zoom-in.svg?react"
import ZoomOutIcon from "@/icons/zoom-out.svg?react"
import "./Lightbox.css"
const isTouchDevice = window.ontouchstart !== undefined
@ -158,7 +158,7 @@ export class Lightbox extends Component<LightboxProps> {
render() {
return <div
className="overlay lightbox"
className="overlay dimmed lightbox"
onClick={this.onClick}
onMouseMove={isTouchDevice ? undefined : this.onMouseMove}
>

View file

@ -0,0 +1,47 @@
// gomuks - A Matrix client written in Go.
// Copyright (C) 2024 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import React, { JSX, createContext, useCallback, useState } from "react"
export interface ModalState {
content: JSX.Element
dimmed?: boolean
wrapperClass?: string
onClose?: () => void
}
type openModal = (state: ModalState) => void
export const ModalContext = createContext<openModal>(() =>
console.error("Tried to open modal without being inside context"))
export const ModalWrapper = ({ children }: { children: React.ReactNode }) => {
const [state, setState] = useState<ModalState | null>(null)
const onClose = useCallback(() => {
setState(null)
state?.onClose?.()
}, [state])
return <>
<ModalContext value={setState}>
{children}
</ModalContext>
{state && <div
className={`overlay ${state.wrapperClass ?? "modal"} ${state.dimmed ? "dimmed" : ""}`}
onClick={onClose}
>
{state.content}
</div>}
</>
}

View file

@ -19,7 +19,7 @@ import { useRoomState } from "@/api/statestore"
import { MemDBEvent, MemberEventContent, UnreadType } from "@/api/types"
import { isEventID } from "@/util/validation.ts"
import { ClientContext } from "../ClientContext.ts"
import { LightboxContext } from "../Lightbox.tsx"
import { LightboxContext } from "../modal/Lightbox.tsx"
import { useRoomContext } from "../roomcontext.ts"
import EventMenu from "./EventMenu.tsx"
import { ReplyIDBody } from "./ReplyBody.tsx"

View file

@ -16,7 +16,7 @@
import React, { use } from "react"
import { getAvatarURL } from "@/api/media.ts"
import { MemberEventContent, UserID } from "@/api/types"
import { LightboxContext } from "../../Lightbox.tsx"
import { LightboxContext } from "../../modal/Lightbox.tsx"
import EventContentProps from "./props.ts"
function useChangeDescription(

View file

@ -17,7 +17,7 @@ import { CSSProperties, use } from "react"
import { getEncryptedMediaURL, getMediaURL } from "@/api/media.ts"
import type { EventType, MediaMessageEventContent } from "@/api/types"
import { ImageContainerSize, calculateMediaSize } from "@/util/mediasize.ts"
import { LightboxContext } from "../../Lightbox.tsx"
import { LightboxContext } from "../../modal/Lightbox.tsx"
import DownloadIcon from "@/icons/download.svg?react"
export const useMediaContent = (