Add skeleton loading indicators

This commit is contained in:
Misode
2024-11-20 22:06:29 +01:00
parent 56b2e1a382
commit f3de707224
7 changed files with 109 additions and 41 deletions

View File

@@ -0,0 +1,37 @@
import type { DocAndNode } from '@spyglassmc/core'
import { JsonFileNode } from '@spyglassmc/json'
import { useErrorBoundary } from 'preact/hooks'
import { useDocAndNode, useSpyglass } from '../../contexts/Spyglass.jsx'
import { message } from '../../Utils.js'
import { ErrorPanel } from '../ErrorPanel.jsx'
import { JsonFileView } from './JsonFileView.jsx'
type FileViewProps = {
docAndNode: DocAndNode | undefined,
}
export function FileView({ docAndNode: original }: FileViewProps) {
const { serviceLoading } = useSpyglass()
const [error, errorRetry] = useErrorBoundary()
if (error) {
return <ErrorPanel error={`Error viewing the file: ${message(error)}`} onDismiss={errorRetry} />
}
const docAndNode = useDocAndNode(original)
if (!docAndNode || serviceLoading) {
return <div class="file-view flex flex-col gap-1">
<div class="skeleton rounded-md h-[34px] w-[200px]"></div>
<div class="skeleton rounded-md h-[34px] w-[240px]"></div>
<div class="skeleton rounded-md h-[34px] w-[190px] ml-[18px]"></div>
<div class="skeleton rounded-md h-[34px] w-[130px] ml-[18px]"></div>
<div class="skeleton rounded-md h-[34px] w-[290px]"></div>
</div>
}
const fileNode = docAndNode?.node.children[0]
if (JsonFileNode.is(fileNode)) {
return <JsonFileView docAndNode={docAndNode} node={fileNode.children[0]} />
}
return <ErrorPanel error={`Cannot view file ${docAndNode.doc.uri}`} />
}

View File

@@ -2,35 +2,19 @@ import type { DocAndNode, Range } from '@spyglassmc/core'
import { dissectUri } from '@spyglassmc/java-edition/lib/binder/index.js'
import type { JsonNode } from '@spyglassmc/json'
import { JsonFileNode } from '@spyglassmc/json'
import { useCallback, useErrorBoundary, useMemo } from 'preact/hooks'
import { useLocale } from '../../contexts/index.js'
import { useDocAndNode, useSpyglass } from '../../contexts/Spyglass.jsx'
import { useCallback, useMemo } from 'preact/hooks'
import { useSpyglass } from '../../contexts/Spyglass.jsx'
import { getRootType, simplifyType } from './McdocHelpers.js'
import type { McdocContext } from './McdocRenderer.jsx'
import { McdocRoot } from './McdocRenderer.jsx'
type TreePanelProps = {
type JsonFileViewProps = {
docAndNode: DocAndNode,
onError: (message: string) => unknown,
node: JsonNode,
}
export function Tree({ docAndNode: original, onError }: TreePanelProps) {
const { lang } = useLocale()
export function JsonFileView({ docAndNode, node }: JsonFileViewProps) {
const { service } = useSpyglass()
if (lang === 'none') return <></>
const docAndNode = useDocAndNode(original)
const fileChild = docAndNode.node.children[0]
if (!JsonFileNode.is(fileChild)) {
return <></>
}
const [error] = useErrorBoundary(e => {
onError(`Error rendering the tree: ${e.message}`)
console.error(e)
})
if (error) return <></>
const makeEdit = useCallback((edit: (range: Range) => JsonNode | undefined) => {
if (!service) {
return
@@ -62,15 +46,15 @@ export function Tree({ docAndNode: original, onError }: TreePanelProps) {
}, [docAndNode, service, makeEdit])
const resourceType = useMemo(() => {
if (original.doc.uri.endsWith('/pack.mcmeta')) {
if (docAndNode.doc.uri.endsWith('/pack.mcmeta')) {
return 'pack_mcmeta'
}
if (ctx === undefined) {
return undefined
}
const res = dissectUri(original.doc.uri, ctx)
const res = dissectUri(docAndNode.doc.uri, ctx)
return res?.category
}, [original, ctx])
}, [docAndNode, ctx])
const mcdocType = useMemo(() => {
if (!ctx || !resourceType) {
@@ -81,8 +65,8 @@ export function Tree({ docAndNode: original, onError }: TreePanelProps) {
return type
}, [resourceType, ctx])
return <div class="tree node-root" data-category={getCategory(resourceType)}>
{(ctx && mcdocType) && <McdocRoot type={mcdocType} node={fileChild.children[0]} ctx={ctx} />}
return <div class="file-view tree node-root" data-category={getCategory(resourceType)}>
{(ctx && mcdocType) && <McdocRoot type={mcdocType} node={node} ctx={ctx} />}
</div>
}

View File

@@ -144,9 +144,15 @@ export function ProjectPanel() {
{project.name !== DRAFT_PROJECT.name && <Btn icon="trashcan" label={locale('project.delete')} onClick={onDeleteProject} />}
</BtnMenu>
</div>
<div class="file-view">
<div class="project-files">
{entries === undefined
? <></>
? <div class="p-2 flex flex-col gap-2">
<div class="skeleton-2 rounded h-4 w-24"></div>
<div class="skeleton-2 rounded h-4 w-32 ml-4"></div>
<div class="skeleton-2 rounded h-4 w-24 ml-8"></div>
<div class="skeleton-2 rounded h-4 w-36 ml-8"></div>
<div class="skeleton-2 rounded h-4 w-28"></div>
</div>
: entries.length === 0
? <span>{locale('project.no_files')}</span>
: <TreeView entries={entries} split={path => path.split('/')} group={FolderEntry} leaf={FileEntry} />}

View File

@@ -13,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, Footer, HasPreview, Octicon, PreviewPanel, ProjectPanel, SearchList, SourcePanel, TextInput, Tree, VersionSwitcher } from '../index.js'
import { Ad, Btn, BtnMenu, ErrorPanel, FileCreation, FileView, Footer, HasPreview, Octicon, PreviewPanel, ProjectPanel, SearchList, SourcePanel, TextInput, VersionSwitcher } from '../index.js'
import { getRootDefault } from './McdocHelpers.js'
export const SHARE_KEY = 'share'
@@ -59,7 +59,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
const [sharedSnippetId, setSharedSnippetId] = useSearchParam(SHARE_KEY)
const ignoreChange = useRef(false)
const { value: docAndNode } = useAsync(async () => {
const { value: docAndNode, loading: docLoading } = useAsync(async () => {
let text: string | undefined = undefined
if (currentPreset && sharedSnippetId) {
setSharedSnippetId(undefined)
@@ -386,7 +386,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
</BtnMenu>
</div>
{error && <ErrorPanel error={error} onDismiss={() => setError(null)} />}
{docAndNode && <Tree docAndNode={docAndNode} onError={setError} />}
<FileView docAndNode={docLoading ? undefined : docAndNode} />
<Footer donate={!gen.tags?.includes('partners')} />
</main>
<div class="popup-actions right-actions" style={`--offset: -${8 + actionsShown * 50}px;`}>

View File

@@ -1,10 +1,11 @@
export * from './FileCreation.js'
export * from './FileRenaming.js'
export * from './FileView.jsx'
export * from './GeneratorCard.jsx'
export * from './GeneratorList.jsx'
export * from './JsonFileView.jsx'
export * from './PreviewPanel.js'
export * from './ProjectCreation.js'
export * from './ProjectDeletion.js'
export * from './ProjectPanel.js'
export * from './SourcePanel.js'
export * from './Tree.js'