forked from Mirrors/gomuks
web/lightbox,web/modal: close on esc
This commit is contained in:
parent
2b10509ceb
commit
4572a9c882
4 changed files with 48 additions and 18 deletions
|
@ -69,7 +69,7 @@ export default class Keybindings {
|
|||
}
|
||||
|
||||
private keyUpMap: KeyMap = {
|
||||
"Escape": evt => evt.target === evt.currentTarget && this.context.clearActiveRoom(),
|
||||
// "Escape": evt => evt.target === evt.currentTarget && this.context.clearActiveRoom(),
|
||||
}
|
||||
|
||||
listen(): () => void {
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
//
|
||||
// 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, useLayoutEffect, useState } from "react"
|
||||
import React, { Component, createContext, createRef, useCallback, useLayoutEffect, useState } from "react"
|
||||
import { keyToString } from "../keybindings.ts"
|
||||
import CloseIcon from "@/icons/close.svg?react"
|
||||
import DownloadIcon from "@/icons/download.svg?react"
|
||||
import RotateLeftIcon from "@/icons/rotate-left.svg?react"
|
||||
|
@ -72,12 +73,8 @@ export class Lightbox extends Component<LightboxProps> {
|
|||
zoom = 1
|
||||
rotate = 0
|
||||
maybePanning = false
|
||||
readonly ref: RefObject<HTMLImageElement | null>
|
||||
|
||||
constructor(props: LightboxProps) {
|
||||
super(props)
|
||||
this.ref = createRef<HTMLImageElement>()
|
||||
}
|
||||
readonly ref = createRef<HTMLImageElement>()
|
||||
readonly wrapperRef = createRef<HTMLDivElement>()
|
||||
|
||||
get style() {
|
||||
return {
|
||||
|
@ -87,6 +84,13 @@ export class Lightbox extends Component<LightboxProps> {
|
|||
}
|
||||
}
|
||||
|
||||
close = () => {
|
||||
this.translate = { x: 0, y: 0 }
|
||||
this.rotate = 0
|
||||
this.zoom = 1
|
||||
this.props.onClose()
|
||||
}
|
||||
|
||||
onClick = () => {
|
||||
if (!this.ref.current) {
|
||||
return
|
||||
|
@ -95,10 +99,7 @@ export class Lightbox extends Component<LightboxProps> {
|
|||
this.ref.current.style.cursor = "auto"
|
||||
this.maybePanning = false
|
||||
} else {
|
||||
this.translate = { x: 0, y: 0 }
|
||||
this.rotate = 0
|
||||
this.zoom = 1
|
||||
this.props.onClose()
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +143,14 @@ export class Lightbox extends Component<LightboxProps> {
|
|||
this.ref.current.style.cursor = "grabbing"
|
||||
}
|
||||
|
||||
onKeyDown = (evt: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
const key = keyToString(evt)
|
||||
if (key === "Escape") {
|
||||
this.close()
|
||||
}
|
||||
evt.stopPropagation()
|
||||
}
|
||||
|
||||
transformer = (callback: () => void) => (evt: React.MouseEvent) => {
|
||||
evt.stopPropagation()
|
||||
if (!this.ref.current) {
|
||||
|
@ -153,6 +162,15 @@ export class Lightbox extends Component<LightboxProps> {
|
|||
this.ref.current.style.scale = style.scale
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (
|
||||
this.wrapperRef.current
|
||||
&& (!document.activeElement || !this.wrapperRef.current.contains(document.activeElement))
|
||||
) {
|
||||
this.wrapperRef.current.focus()
|
||||
}
|
||||
}
|
||||
|
||||
stopPropagation = (evt: React.MouseEvent) => evt.stopPropagation()
|
||||
zoomIn = this.transformer(() => this.zoom = Math.min(this.zoom * 1.1, 10))
|
||||
zoomOut = this.transformer(() => this.zoom = Math.max(this.zoom / 1.1, 0.01))
|
||||
|
@ -164,6 +182,9 @@ export class Lightbox extends Component<LightboxProps> {
|
|||
className="overlay dimmed lightbox"
|
||||
onClick={this.onClick}
|
||||
onMouseMove={isTouchDevice ? undefined : this.onMouseMove}
|
||||
tabIndex={-1}
|
||||
onKeyDown={this.onKeyDown}
|
||||
ref={this.wrapperRef}
|
||||
>
|
||||
<div className="controls" onClick={this.stopPropagation}>
|
||||
<button onClick={this.zoomOut}><ZoomOutIcon/></button>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//
|
||||
// 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, useReducer } from "react"
|
||||
import React, { JSX, createContext, useCallback, useLayoutEffect, useReducer, useRef } from "react"
|
||||
|
||||
export interface ModalState {
|
||||
content: JSX.Element
|
||||
|
@ -44,13 +44,22 @@ export const ModalWrapper = ({ children }: { children: React.ReactNode }) => {
|
|||
if (evt.key === "Escape") {
|
||||
setState(null)
|
||||
}
|
||||
evt.stopPropagation()
|
||||
}, [])
|
||||
const wrapperRef = useRef<HTMLDivElement>(null)
|
||||
useLayoutEffect(() => {
|
||||
if (wrapperRef.current && (!document.activeElement || !wrapperRef.current.contains(document.activeElement))) {
|
||||
wrapperRef.current.focus()
|
||||
}
|
||||
}, [state])
|
||||
return <ModalContext value={setState}>
|
||||
{children}
|
||||
{state && <div
|
||||
className={`overlay ${state.wrapperClass ?? "modal"} ${state.dimmed ? "dimmed" : ""}`}
|
||||
onClick={onClickWrapper}
|
||||
onKeyDown={onKeyWrapper}
|
||||
tabIndex={-1}
|
||||
ref={wrapperRef}
|
||||
>
|
||||
<ModalCloseContext value={onClickWrapper}>
|
||||
{state.content}
|
||||
|
|
|
@ -28,7 +28,7 @@ interface ConfirmWithMessageProps {
|
|||
onConfirm: (reason: string) => void
|
||||
}
|
||||
|
||||
const ConfirmWithMessageProps = ({
|
||||
const ConfirmWithMessageModal = ({
|
||||
evt, title, description, placeholder, confirmButton, onConfirm,
|
||||
}: ConfirmWithMessageProps) => {
|
||||
const [reason, setReason] = useState("")
|
||||
|
@ -48,7 +48,7 @@ const ConfirmWithMessageProps = ({
|
|||
<div className="confirm-description">
|
||||
{description}
|
||||
</div>
|
||||
<input value={reason} type="text" placeholder={placeholder} onChange={onChangeReason} />
|
||||
<input autoFocus value={reason} type="text" placeholder={placeholder} onChange={onChangeReason} />
|
||||
<div className="confirm-buttons">
|
||||
<button onClick={closeModal}>Cancel</button>
|
||||
<button onClick={onConfirmWrapped}>{confirmButton}</button>
|
||||
|
@ -56,4 +56,4 @@ const ConfirmWithMessageProps = ({
|
|||
</div>
|
||||
}
|
||||
|
||||
export default ConfirmWithMessageProps
|
||||
export default ConfirmWithMessageModal
|
||||
|
|
Loading…
Add table
Reference in a new issue