mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-24 15:47:08 +00:00
Refactor modals to use context provider
This commit is contained in:
@@ -5,6 +5,7 @@ import type { Method } from '../../Analytics.js'
|
||||
import { Analytics } from '../../Analytics.js'
|
||||
import type { ConfigGenerator } from '../../Config.js'
|
||||
import { getProjectRoot, useLocale, useProject, useVersion } from '../../contexts/index.js'
|
||||
import { useModal } from '../../contexts/Modal.jsx'
|
||||
import { useSpyglass } from '../../contexts/Spyglass.jsx'
|
||||
import { genPath, message } from '../../Utils.js'
|
||||
import { Btn } from '../Btn.js'
|
||||
@@ -15,12 +16,11 @@ interface Props {
|
||||
docAndNode: DocAndNode,
|
||||
gen: ConfigGenerator,
|
||||
method: Method,
|
||||
onCreate: (uri: string) => void,
|
||||
onClose: () => void,
|
||||
}
|
||||
export function FileCreation({ docAndNode, gen, method, onCreate, onClose }: Props) {
|
||||
export function FileCreation({ docAndNode, gen, method }: Props) {
|
||||
const { locale } = useLocale()
|
||||
const { version } = useVersion()
|
||||
const { hideModal } = useModal()
|
||||
const { project } = useProject()
|
||||
const { client } = useSpyglass()
|
||||
|
||||
@@ -42,15 +42,15 @@ export function FileCreation({ docAndNode, gen, method, onCreate, onClose }: Pro
|
||||
Analytics.saveProjectFile(method)
|
||||
const text = docAndNode.doc.getText()
|
||||
client.fs.writeFile(uri, text).then(() => {
|
||||
onCreate(uri)
|
||||
hideModal()
|
||||
}).catch((e) => {
|
||||
setError(message(e))
|
||||
})
|
||||
}, [version, project, client, fileId ])
|
||||
|
||||
return <Modal class="file-modal" onDismiss={onClose}>
|
||||
return <Modal class="file-modal">
|
||||
<p>{locale('project.save_current_file')}</p>
|
||||
<TextInput autofocus={gen.id !== 'pack_mcmeta'} class="btn btn-input" value={fileId} onChange={changeFileId} onEnter={doSave} onCancel={onClose} placeholder={locale('resource_location')} spellcheck={false} readOnly={gen.id === 'pack_mcmeta'} />
|
||||
<TextInput autofocus={gen.id !== 'pack_mcmeta'} class="btn btn-input" value={fileId} onChange={changeFileId} onEnter={doSave} onCancel={hideModal} placeholder={locale('resource_location')} spellcheck={false} readOnly={gen.id === 'pack_mcmeta'} />
|
||||
{error !== undefined && <span class="invalid">{error}</span>}
|
||||
<Btn icon="file" label={locale('project.save')} onClick={doSave} />
|
||||
</Modal>
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { useState } from 'preact/hooks'
|
||||
import { Analytics } from '../../Analytics.js'
|
||||
import { useLocale } from '../../contexts/index.js'
|
||||
import { useModal } from '../../contexts/Modal.jsx'
|
||||
import { Btn } from '../Btn.js'
|
||||
import { TextInput } from '../forms/index.js'
|
||||
import { Modal } from '../Modal.js'
|
||||
|
||||
interface Props {
|
||||
uri: string,
|
||||
onClose: () => void,
|
||||
}
|
||||
export function FileRenaming({ uri, onClose }: Props) {
|
||||
export function FileRenaming({ uri }: Props) {
|
||||
const { locale } = useLocale()
|
||||
const { hideModal } = useModal()
|
||||
const [fileId, setFileId] = useState(uri) // TODO: get original file id
|
||||
const [error, setError] = useState<string>()
|
||||
|
||||
@@ -26,12 +27,12 @@ export function FileRenaming({ uri, onClose }: Props) {
|
||||
}
|
||||
Analytics.renameProjectFile('menu')
|
||||
// TODO: rename file
|
||||
onClose()
|
||||
hideModal()
|
||||
}
|
||||
|
||||
return <Modal class="file-modal" onDismiss={onClose}>
|
||||
return <Modal class="file-modal">
|
||||
<p>{locale('project.rename_file')}</p>
|
||||
<TextInput autofocus class="btn btn-input" value={fileId} onChange={changeFileId} onEnter={doSave} onCancel={onClose} placeholder={locale('resource_location')} spellcheck={false} />
|
||||
<TextInput autofocus class="btn btn-input" value={fileId} onChange={changeFileId} onEnter={doSave} onCancel={hideModal} placeholder={locale('resource_location')} spellcheck={false} />
|
||||
{error !== undefined && <span class="invalid">{error}</span>}
|
||||
<Btn icon="pencil" label={locale('project.rename')} onClick={doSave} />
|
||||
</Modal>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useMemo, useState } from 'preact/hooks'
|
||||
import config from '../../Config.js'
|
||||
import { useLocale, useProject } from '../../contexts/index.js'
|
||||
import { useModal } from '../../contexts/Modal.jsx'
|
||||
import { useSpyglass } from '../../contexts/Spyglass.jsx'
|
||||
import type { VersionId } from '../../services/index.js'
|
||||
import { DEFAULT_VERSION } from '../../services/index.js'
|
||||
@@ -9,11 +10,9 @@ import { hexId, readZip } from '../../Utils.js'
|
||||
import { Btn, BtnMenu, FileUpload, Octicon, TextInput } from '../index.js'
|
||||
import { Modal } from '../Modal.js'
|
||||
|
||||
interface Props {
|
||||
onClose: () => unknown,
|
||||
}
|
||||
export function ProjectCreation({ onClose }: Props) {
|
||||
export function ProjectCreation() {
|
||||
const { locale } = useLocale()
|
||||
const { hideModal } = useModal()
|
||||
const { projects, createProject, changeProject } = useProject()
|
||||
const { client } = useSpyglass()
|
||||
|
||||
@@ -45,12 +44,13 @@ export function ProjectCreation({ onClose }: Props) {
|
||||
const path = entry[0].startsWith('/') ? entry[0].slice(1) : entry[0]
|
||||
return client.fs.writeFile(rootUri + path, entry[1])
|
||||
}))
|
||||
onClose()
|
||||
hideModal()
|
||||
}).catch(() => {
|
||||
onClose()
|
||||
// TODO: handle errors
|
||||
hideModal()
|
||||
})
|
||||
} else {
|
||||
onClose()
|
||||
hideModal()
|
||||
}
|
||||
}, [createProject, changeProject, client, version, name, namespace, file])
|
||||
|
||||
@@ -64,7 +64,7 @@ export function ProjectCreation({ onClose }: Props) {
|
||||
|
||||
const versions = config.versions.map(v => v.id as VersionId).reverse()
|
||||
|
||||
return <Modal class="project-creation" onDismiss={onClose}>
|
||||
return <Modal class="project-creation">
|
||||
<p>{locale('project.create')}</p>
|
||||
<div class="input-group">
|
||||
<TextInput autofocus class={`btn btn-input${!creating && (invalidName || name.length === 0) ? ' invalid': ''}`} placeholder={locale('project.name')} value={name} onChange={setName} />
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import { useCallback } from 'preact/hooks'
|
||||
import { Analytics } from '../../Analytics.js'
|
||||
import { useLocale, useProject } from '../../contexts/index.js'
|
||||
import { useModal } from '../../contexts/Modal.jsx'
|
||||
import { Btn } from '../Btn.js'
|
||||
import { Modal } from '../Modal.js'
|
||||
|
||||
interface Props {
|
||||
onClose: () => void,
|
||||
}
|
||||
export function ProjectDeletion({ onClose }: Props) {
|
||||
export function ProjectDeletion() {
|
||||
const { locale } = useLocale()
|
||||
const { hideModal } = useModal()
|
||||
const { project, deleteProject } = useProject()
|
||||
|
||||
const doSave = useCallback(() => {
|
||||
Analytics.deleteProject('menu')
|
||||
deleteProject(project.name)
|
||||
onClose()
|
||||
}, [onClose, deleteProject])
|
||||
hideModal()
|
||||
}, [deleteProject, hideModal])
|
||||
|
||||
return <Modal class="file-modal" onDismiss={onClose}>
|
||||
return <Modal class="file-modal">
|
||||
<p>{locale('project.delete_confirm.1', project.name)}</p>
|
||||
<p><b>{locale('project.delete_confirm.2')}</b></p>
|
||||
<div class="button-group">
|
||||
<Btn icon="trashcan" label={locale('project.delete')} onClick={doSave} class="danger" />
|
||||
<Btn label={locale('project.cancel')} onClick={onClose} />
|
||||
<Btn label={locale('project.cancel')} onClick={hideModal} />
|
||||
</div>
|
||||
</Modal>
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { route } from 'preact-router'
|
||||
import { useCallback, useMemo, useRef } from 'preact/hooks'
|
||||
import config from '../../Config.js'
|
||||
import { DRAFT_PROJECT, getProjectRoot, useLocale, useProject } from '../../contexts/index.js'
|
||||
import { useModal } from '../../contexts/Modal.jsx'
|
||||
import { useSpyglass } from '../../contexts/Spyglass.jsx'
|
||||
import { useAsync } from '../../hooks/useAsync.js'
|
||||
import { useFocus } from '../../hooks/useFocus.js'
|
||||
@@ -11,15 +12,13 @@ import { BtnMenu } from '../BtnMenu.js'
|
||||
import { Octicon } from '../Octicon.jsx'
|
||||
import type { TreeViewGroupRenderer, TreeViewLeafRenderer } from '../TreeView.js'
|
||||
import { TreeView } from '../TreeView.js'
|
||||
import { FileRenaming } from './FileRenaming.jsx'
|
||||
import { ProjectCreation } from './ProjectCreation.jsx'
|
||||
import { ProjectDeletion } from './ProjectDeletion.jsx'
|
||||
|
||||
interface Props {
|
||||
onError: (message: string) => void,
|
||||
onRename: (uri: string) => void,
|
||||
onCreateProject: () => void,
|
||||
onDeleteProject: () => void,
|
||||
}
|
||||
export function ProjectPanel({ onRename, onCreateProject, onDeleteProject}: Props) {
|
||||
export function ProjectPanel() {
|
||||
const { locale } = useLocale()
|
||||
const { showModal } = useModal()
|
||||
const { projects, project, projectUri, setProjectUri, changeProject } = useProject()
|
||||
const { client, service } = useSpyglass()
|
||||
|
||||
@@ -47,12 +46,20 @@ export function ProjectPanel({ onRename, onCreateProject, onDeleteProject}: Prop
|
||||
download.current.click()
|
||||
}
|
||||
|
||||
const onDeleteProject = useCallback(() => {
|
||||
showModal(() => <ProjectDeletion />)
|
||||
}, [])
|
||||
|
||||
const onCreateProject = useCallback(() => {
|
||||
showModal(() => <ProjectCreation />)
|
||||
}, [])
|
||||
|
||||
const actions = useMemo(() => [
|
||||
{
|
||||
icon: 'pencil',
|
||||
label: locale('project.rename_file'),
|
||||
onAction: (uri: string) => {
|
||||
onRename(uri)
|
||||
showModal(() => <FileRenaming uri={uri} />)
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -64,7 +71,7 @@ export function ProjectPanel({ onRename, onCreateProject, onDeleteProject}: Prop
|
||||
})
|
||||
},
|
||||
},
|
||||
], [client, onRename, projectRoot])
|
||||
], [client, projectRoot, showModal])
|
||||
|
||||
const FolderEntry: TreeViewGroupRenderer = useCallback(({ name, open, onClick }) => {
|
||||
return <div class="entry" onClick={onClick} >
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Analytics } from '../../Analytics.js'
|
||||
import type { ConfigGenerator } from '../../Config.js'
|
||||
import config from '../../Config.js'
|
||||
import { DRAFT_PROJECT, useLocale, useProject, useVersion } from '../../contexts/index.js'
|
||||
import { useModal } from '../../contexts/Modal.jsx'
|
||||
import { useSpyglass, watchSpyglassUri } from '../../contexts/Spyglass.jsx'
|
||||
import { AsyncCancel, useActiveTimeout, useAsync, useSearchParam } from '../../hooks/index.js'
|
||||
import type { VersionId } from '../../services/index.js'
|
||||
@@ -12,7 +13,7 @@ import { checkVersion, fetchDependencyMcdoc, fetchPreset, fetchRegistries, getSn
|
||||
import { DEPENDENCY_URI } from '../../services/Spyglass.js'
|
||||
import { Store } from '../../Store.js'
|
||||
import { cleanUrl, genPath } from '../../Utils.js'
|
||||
import { Ad, Btn, BtnMenu, ErrorPanel, FileCreation, FileRenaming, Footer, HasPreview, Octicon, PreviewPanel, ProjectCreation, ProjectDeletion, ProjectPanel, SearchList, SourcePanel, TextInput, Tree, VersionSwitcher } from '../index.js'
|
||||
import { Ad, Btn, BtnMenu, ErrorPanel, FileCreation, Footer, HasPreview, Octicon, PreviewPanel, ProjectPanel, SearchList, SourcePanel, TextInput, Tree, VersionSwitcher } from '../index.js'
|
||||
import { getRootDefault } from './McdocHelpers.js'
|
||||
|
||||
export const SHARE_KEY = 'share'
|
||||
@@ -25,6 +26,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
const { locale } = useLocale()
|
||||
const { version, changeVersion, changeTargetVersion } = useVersion()
|
||||
const { service } = useSpyglass()
|
||||
const { showModal } = useModal()
|
||||
const { project, projectUri, setProjectUri, updateProject } = useProject()
|
||||
const [error, setError] = useState<Error | string | null>(null)
|
||||
const [errorBoundary, errorRetry] = useErrorBoundary()
|
||||
@@ -164,7 +166,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
Analytics.redoGenerator(gen.id, 1, 'hotkey')
|
||||
await service.redoEdit(uri)
|
||||
} else if (e.ctrlKey && e.key === 's') {
|
||||
setFileSaving('hotkey')
|
||||
saveFile('hotkey')
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
@@ -302,7 +304,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
const [projectShown, setProjectShown] = useState(Store.getProjectPanelOpen() ?? window.innerWidth > 1000)
|
||||
const [projectShown, setProjectShown] = useState(Store.getProjectPanelOpen() ?? false)
|
||||
const toggleProjectShown = useCallback(() => {
|
||||
if (projectShown) {
|
||||
Analytics.hideProject('menu')
|
||||
@@ -313,15 +315,22 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
setProjectShown(!projectShown)
|
||||
}, [projectShown])
|
||||
|
||||
const [projectCreating, setProjectCreating] = useState(false)
|
||||
const [projectDeleting, setprojectDeleting] = useState(false)
|
||||
const [fileSaving, setFileSaving] = useState<Method | undefined>(undefined)
|
||||
const [fileRenaming, setFileRenaming] = useState<string | undefined>(undefined)
|
||||
const saveFile = useCallback((method: Method) => {
|
||||
if (!docAndNode) {
|
||||
return
|
||||
}
|
||||
showModal(() => <FileCreation gen={gen} docAndNode={docAndNode} method={method} />)
|
||||
}, [showModal, gen, docAndNode])
|
||||
|
||||
const onNewFile = useCallback(() => {
|
||||
const newEmptyFile = useCallback(async () => {
|
||||
if (service) {
|
||||
const unsavedUri = service.getUnsavedFileUri(gen)
|
||||
const node = getRootDefault(gen.id, service.getCheckerContext())
|
||||
const text = service.formatNode(node, unsavedUri)
|
||||
await service.writeFile(unsavedUri, text)
|
||||
}
|
||||
setProjectUri(undefined)
|
||||
// TODO: create new file with default contents
|
||||
}, [setProjectUri])
|
||||
}, [showModal])
|
||||
|
||||
return <>
|
||||
<main class={`${previewShown ? 'has-preview' : ''} ${projectShown ? 'has-project' : ''}`}>
|
||||
@@ -339,8 +348,8 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
<Btn icon="history" label={locale('reset_default')} onClick={reset} />
|
||||
<Btn icon="arrow_left" label={locale('undo')} onClick={undo} />
|
||||
<Btn icon="arrow_right" label={locale('redo')} onClick={redo} />
|
||||
<Btn icon="plus_circle" label={locale('project.new_file')} onClick={onNewFile} />
|
||||
<Btn icon="file" label={locale('project.save')} onClick={() => setFileSaving('menu')} />
|
||||
<Btn icon="plus_circle" label={locale('project.new_file')} onClick={newEmptyFile} />
|
||||
<Btn icon="file" label={locale('project.save')} onClick={() => saveFile('menu')} />
|
||||
</BtnMenu>
|
||||
</div>
|
||||
{error && <ErrorPanel error={error} onDismiss={() => setError(null)} />}
|
||||
@@ -380,11 +389,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
<div class={`popup-project${projectShown ? ' shown' : ''}`}>
|
||||
<ProjectPanel onError={setError} onRename={setFileRenaming} onDeleteProject={() => setprojectDeleting(true)} onCreateProject={() => setProjectCreating(true)} />
|
||||
<ProjectPanel />
|
||||
</div>
|
||||
{projectCreating && <ProjectCreation onClose={() => setProjectCreating(false)} />}
|
||||
{projectDeleting && <ProjectDeletion onClose={() => setprojectDeleting(false)} />}
|
||||
{docAndNode && fileSaving && <FileCreation gen={gen} docAndNode={docAndNode} method={fileSaving} onCreate={(uri) => {setFileSaving(undefined); setProjectUri(uri)}} onClose={() => setFileSaving(undefined)} />}
|
||||
{fileRenaming && <FileRenaming uri={fileRenaming} onClose={() => setFileRenaming(undefined)} />}
|
||||
</>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user