forked from Mirrors/gomuks
web/modal: add boxing to modal utility
This commit is contained in:
parent
303ea43834
commit
f4be132313
6 changed files with 48 additions and 54 deletions
|
@ -8,6 +8,21 @@ div.overlay {
|
||||||
&.dimmed {
|
&.dimmed {
|
||||||
background-color: var(--dimmed-overlay-background-color);
|
background-color: var(--dimmed-overlay-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.modal > div.modal-box {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
border-radius: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
max-width: min(80rem, 80vw);
|
||||||
|
max-height: min(80rem, 80vh);
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> div.modal-box-inner {
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.lightbox {
|
div.lightbox {
|
||||||
|
|
|
@ -18,7 +18,9 @@ import React, { JSX, createContext, useCallback, useLayoutEffect, useReducer, us
|
||||||
export interface ModalState {
|
export interface ModalState {
|
||||||
content: JSX.Element
|
content: JSX.Element
|
||||||
dimmed?: boolean
|
dimmed?: boolean
|
||||||
wrapperClass?: string
|
boxed?: boolean
|
||||||
|
boxClass?: string
|
||||||
|
innerBoxClass?: string
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,18 +54,28 @@ export const ModalWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||||
wrapperRef.current.focus()
|
wrapperRef.current.focus()
|
||||||
}
|
}
|
||||||
}, [state])
|
}, [state])
|
||||||
return <ModalContext value={setState}>
|
let modal: JSX.Element | null = null
|
||||||
{children}
|
if (state) {
|
||||||
{state && <div
|
let content = <ModalCloseContext value={onClickWrapper}>{state.content}</ModalCloseContext>
|
||||||
className={`overlay ${state.wrapperClass ?? "modal"} ${state.dimmed ? "dimmed" : ""}`}
|
if (state.boxed) {
|
||||||
|
content = <div className={`modal-box ${state.boxClass ?? ""}`}>
|
||||||
|
<div className={`modal-box-inner ${state.innerBoxClass ?? ""}`}>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
modal = <div
|
||||||
|
className={`overlay modal ${state.dimmed ? "dimmed" : ""}`}
|
||||||
onClick={onClickWrapper}
|
onClick={onClickWrapper}
|
||||||
onKeyDown={onKeyWrapper}
|
onKeyDown={onKeyWrapper}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
ref={wrapperRef}
|
ref={wrapperRef}
|
||||||
>
|
>
|
||||||
<ModalCloseContext value={onClickWrapper}>
|
{content}
|
||||||
{state.content}
|
</div>
|
||||||
</ModalCloseContext>
|
}
|
||||||
</div>}
|
return <ModalContext value={setState}>
|
||||||
|
{children}
|
||||||
|
{modal}
|
||||||
</ModalContext>
|
</ModalContext>
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ const ConfirmWithMessageModal = ({
|
||||||
const onChangeReason = useCallback((evt: React.ChangeEvent<HTMLInputElement>) => {
|
const onChangeReason = useCallback((evt: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setReason(evt.target.value)
|
setReason(evt.target.value)
|
||||||
}, [])
|
}, [])
|
||||||
return <div className="confirm-message-modal">
|
return <>
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
<div className="timeline-event-container">
|
<div className="timeline-event-container">
|
||||||
<TimelineEvent evt={evt} prevEvt={null} disableMenu={true} />
|
<TimelineEvent evt={evt} prevEvt={null} disableMenu={true} />
|
||||||
|
@ -53,7 +53,7 @@ const ConfirmWithMessageModal = ({
|
||||||
<button onClick={closeModal}>Cancel</button>
|
<button onClick={closeModal}>Cancel</button>
|
||||||
<button onClick={onConfirmWrapped}>{confirmButton}</button>
|
<button onClick={onConfirmWrapped}>{confirmButton}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConfirmWithMessageModal
|
export default ConfirmWithMessageModal
|
||||||
|
|
|
@ -19,8 +19,8 @@ import { MemDBEvent, PowerLevelEventContent } from "@/api/types"
|
||||||
import ClientContext from "../../ClientContext.ts"
|
import ClientContext from "../../ClientContext.ts"
|
||||||
import { ModalCloseContext, ModalContext } from "../../modal/Modal.tsx"
|
import { ModalCloseContext, ModalContext } from "../../modal/Modal.tsx"
|
||||||
import { RoomContext, RoomContextData } from "../../roomview/roomcontext.ts"
|
import { RoomContext, RoomContextData } from "../../roomview/roomcontext.ts"
|
||||||
|
import JSONView from "../../util/JSONView.tsx"
|
||||||
import ConfirmWithMessageModal from "./ConfirmWithMessageModal.tsx"
|
import ConfirmWithMessageModal from "./ConfirmWithMessageModal.tsx"
|
||||||
import ViewSourceModal from "./ViewSourceModal.tsx"
|
|
||||||
import ViewSourceIcon from "@/icons/code.svg?react"
|
import ViewSourceIcon from "@/icons/code.svg?react"
|
||||||
import DeleteIcon from "@/icons/delete.svg?react"
|
import DeleteIcon from "@/icons/delete.svg?react"
|
||||||
import PinIcon from "@/icons/pin.svg?react"
|
import PinIcon from "@/icons/pin.svg?react"
|
||||||
|
@ -39,11 +39,17 @@ const EventExtraMenu = ({ evt, room, style }: EventExtraMenuProps) => {
|
||||||
const closeModal = use(ModalCloseContext)
|
const closeModal = use(ModalCloseContext)
|
||||||
const openModal = use(ModalContext)
|
const openModal = use(ModalContext)
|
||||||
const onClickViewSource = useCallback(() => {
|
const onClickViewSource = useCallback(() => {
|
||||||
openModal({ dimmed: true, content: <ViewSourceModal evt={evt}/> })
|
openModal({
|
||||||
|
dimmed: true,
|
||||||
|
boxed: true,
|
||||||
|
content: <JSONView data={evt} />,
|
||||||
|
})
|
||||||
}, [evt, openModal])
|
}, [evt, openModal])
|
||||||
const onClickReport = useCallback(() => {
|
const onClickReport = useCallback(() => {
|
||||||
openModal({
|
openModal({
|
||||||
dimmed: true,
|
dimmed: true,
|
||||||
|
boxed: true,
|
||||||
|
innerBoxClass: "confirm-message-modal",
|
||||||
content: <RoomContext value={new RoomContextData(room)}>
|
content: <RoomContext value={new RoomContextData(room)}>
|
||||||
<ConfirmWithMessageModal
|
<ConfirmWithMessageModal
|
||||||
evt={evt}
|
evt={evt}
|
||||||
|
@ -62,6 +68,8 @@ const EventExtraMenu = ({ evt, room, style }: EventExtraMenuProps) => {
|
||||||
const onClickRedact = useCallback(() => {
|
const onClickRedact = useCallback(() => {
|
||||||
openModal({
|
openModal({
|
||||||
dimmed: true,
|
dimmed: true,
|
||||||
|
boxed: true,
|
||||||
|
innerBoxClass: "confirm-message-modal",
|
||||||
content: <RoomContext value={new RoomContextData(room)}>
|
content: <RoomContext value={new RoomContextData(room)}>
|
||||||
<ConfirmWithMessageModal
|
<ConfirmWithMessageModal
|
||||||
evt={evt}
|
evt={evt}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
// 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 { MemDBEvent } from "@/api/types"
|
|
||||||
import JSONView from "../../util/JSONView.tsx"
|
|
||||||
|
|
||||||
interface ViewSourceModalProps {
|
|
||||||
evt: MemDBEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
const ViewSourceModal = ({ evt }: ViewSourceModalProps) => {
|
|
||||||
return <div className="view-source-modal">
|
|
||||||
<JSONView data={evt} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ViewSourceModal
|
|
|
@ -45,21 +45,9 @@ div.event-context-menu-extra {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.view-source-modal {
|
|
||||||
max-width: min(80rem, 80vw);
|
|
||||||
max-height: min(80rem, 80vh);
|
|
||||||
overflow: auto;
|
|
||||||
background-color: var(--background-color);
|
|
||||||
border-radius: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.confirm-message-modal {
|
div.confirm-message-modal {
|
||||||
width: min(40rem, 80vw);
|
width: min(40rem, 80vw);
|
||||||
max-height: min(40rem, 80vh);
|
max-height: min(40rem, 80vh);
|
||||||
background-color: var(--background-color);
|
|
||||||
border-radius: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
|
|
Loading…
Add table
Reference in a new issue