refactor modal stacks so they are:

1. watchable for changes
2. can be used along with react
This commit is contained in:
Vitaly 2023-08-22 05:01:24 +03:00
commit 46bc423935
2 changed files with 25 additions and 15 deletions

View file

@ -1,7 +1,7 @@
//@ts-check
const { LitElement, html, css } = require('lit')
const { isMobile } = require('./menus/components/common')
const { activeModalStack, hideCurrentModal } = require('./globalState')
const { activeModalStack, hideCurrentModal, showModal } = require('./globalState')
import { repeat } from 'lit/directives/repeat.js'
import { classMap } from 'lit/directives/class-map.js'
@ -168,7 +168,7 @@ class ChatBox extends LitElement {
this.shadowRoot.getElementById('chat-wrapper2').classList.toggle('input-mobile', isMobile())
this.shadowRoot.getElementById('chat-wrapper').classList.toggle('display-mobile', isMobile())
activeModalStack.push(this)
showModal(this)
// Exit the pointer lock
document.exitPointerLock()
@ -187,7 +187,7 @@ class ChatBox extends LitElement {
}
get inChat () {
return activeModalStack.includes(this)
return activeModalStack.find(m => m.elem === this) !== undefined
}
/**

View file

@ -1,17 +1,17 @@
//@ts-check
import { proxy } from 'valtio'
import { proxy, ref } from 'valtio'
import { pointerLock } from './utils'
// todo: refactor structure with support of hideNext=false
/**
* @typedef {(HTMLElement & Record<string, any>)} Modal
* @typedef {({elem?: HTMLElement & Record<string, any>} & {reactType?: string})} Modal
* @typedef {{callback, label}} ContextMenuItem
*/
/** @type {Modal[]} */
export const activeModalStack = []
export const activeModalStack = proxy([])
export const replaceActiveModalStack = (name, newModalStack = activeModalStacks[name]) => {
hideModal(undefined, undefined, { restorePrevious: false, force: true, })
@ -26,18 +26,28 @@ window.activeModalStack = activeModalStack
export const customDisplayManageKeyword = 'custom'
const defaultModalActions = {
show (/** @type {Modal} */modal) {
if (modal.elem) modal.elem.style.display = 'block'
},
hide (/** @type {Modal} */modal) {
if (modal.elem) modal.elem.style.display = 'none'
}
}
const showModalInner = (/** @type {Modal} */ modal) => {
const cancel = modal.show?.()
const cancel = modal.elem?.show?.()
if (cancel && cancel !== customDisplayManageKeyword) return false
if (cancel !== 'custom') modal.style.display = 'block'
if (cancel !== 'custom') defaultModalActions.show(modal)
return true
}
export const showModal = (/** @type {Modal} */ modal) => {
export const showModal = (/** @type {HTMLElement & Record<string, any> | {reactType: string}} */ elem) => {
const resolved = elem instanceof HTMLElement ? { elem: ref(elem) } : elem
const curModal = activeModalStack.slice(-1)[0]
if (modal === curModal || !showModalInner(modal)) return
if (curModal) curModal.style.display = 'none'
activeModalStack.push(modal)
if (elem === curModal?.elem || !showModalInner(resolved)) return
if (curModal) defaultModalActions.hide(curModal)
activeModalStack.push(resolved)
}
/**
@ -49,13 +59,13 @@ export const showModal = (/** @type {Modal} */ modal) => {
export const hideModal = (modal = activeModalStack.slice(-1)[0], data = undefined, options = {}) => {
const { force = false, restorePrevious = true } = options
if (!modal) return
let cancel = modal.hide?.(data)
let cancel = modal.elem?.hide?.(data)
if (force && cancel !== customDisplayManageKeyword) {
cancel = undefined
}
if (!cancel || cancel === customDisplayManageKeyword) {
if (cancel !== customDisplayManageKeyword) modal.style.display = 'none'
if (cancel !== customDisplayManageKeyword) defaultModalActions.hide(modal)
activeModalStack.pop()
const newModal = activeModalStack.slice(-1)[0]
if (newModal && restorePrevious) {
@ -91,7 +101,7 @@ export const showContextmenu = (/** @type {ContextMenuItem[]} */items, { clientX
// ---
export const isGameActive = (foregroundCheck = false) => {
export const isGameActive = (foregroundCheck) => {
if (foregroundCheck && activeModalStack.length) return false
return document.getElementById('hud').style.display !== 'none'
}