import type { Child } from 'hono/jsx'
import { render } from 'hono/jsx/dom'
import { define } from '@because/forge'
import { theme } from '../themes'
let modalTitle: string | null = null
let modalContent: (() => Child) | null = null
const renderModal = () => {
const root = document.getElementById('modal')
if (root) render(, root)
}
export const openModal = (title: string, content: () => Child) => {
modalTitle = title
modalContent = content
renderModal()
requestAnimationFrame(() => {
document.querySelector('[data-modal-body] input')?.focus()
})
}
export const closeModal = () => {
modalTitle = null
modalContent = null
renderModal()
}
export const rerenderModal = () => {
renderModal()
}
// ESC key handler
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modalContent) {
closeModal()
}
})
const ModalBackdrop = define('ModalBackdrop', {
position: 'fixed',
inset: 0,
background: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'center',
paddingTop: '20vh',
zIndex: 1000,
})
const ModalBox = define('ModalBox', {
background: theme('colors-bg'),
borderRadius: theme('radius-md'),
border: `1px solid ${theme('colors-border')}`,
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.2)',
maxWidth: 500,
width: '90%',
maxHeight: '80vh',
overflow: 'auto',
})
const ModalHeader = define('ModalHeader', {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '16px 20px',
borderBottom: `1px solid ${theme('colors-border')}`,
})
const ModalTitle = define('ModalTitle', {
fontSize: 16,
fontWeight: 600,
margin: 0,
})
const ModalCloseButton = define('ModalCloseButton', {
base: 'button',
background: 'none',
border: 'none',
cursor: 'pointer',
padding: 4,
fontSize: 18,
color: theme('colors-textMuted'),
lineHeight: 1,
selectors: {
'&:hover': { color: theme('colors-text') },
},
})
const ModalBody = define('ModalBody', {
padding: 20,
})
export const Modal = () => {
if (!modalContent) return null
return (
e.stopPropagation()}>
{modalTitle}
×
{modalContent()}
)
}