diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index 1011c707..a777c2e4 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -8,7 +8,7 @@ import { handleAttributes } from '@spyglassmc/mcdoc/lib/runtime/attribute/index. import type { SimplifiedEnum, SimplifiedMcdocType, SimplifiedMcdocTypeNoUnion, SimplifiedStructType, SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { getValues } from '@spyglassmc/mcdoc/lib/runtime/completer/index.js' import { Identifier, ItemStack } from 'deepslate' -import { useCallback, useMemo } from 'preact/hooks' +import { useCallback, useMemo, useState } from 'preact/hooks' import config from '../../Config.js' import { useLocale } from '../../contexts/Locale.jsx' import { useFocus } from '../../hooks/useFocus.js' @@ -440,10 +440,13 @@ function StructHead({ type: outerType, optional, node, makeEdit, ctx }: Props) { - const { locale } = useLocale() if (!JsonObjectNode.is(node)) { return <> } + + const { locale } = useLocale() + const { expand, collapse, isToggled } = useToggles() + const type = node.typeDef?.kind === 'struct' ? node.typeDef : outerType // For some reason spyglass can include fields that haven't been filtered out in node.typeDef const fields = type.fields.filter(field => { @@ -588,6 +591,9 @@ function StructBody({ type: outerType, node, makeEdit, ctx }: Props } const child = pair.value + const canToggle = JsonObjectNode.is(child) + const toggled = isToggled(key) + const isCollapsed = canToggle && (toggled === false || (toggled === undefined && (node.children.length - staticChilds.length) > 20)) // TODO: correctly determine which dynamic field this is a key for const field = dynamicFields[0] as SimplifiedStructTypePairField | undefined if (!field) { @@ -613,13 +619,17 @@ function StructBody({ type: outerType, node, makeEdit, ctx }: Props
+ {canToggle && (isCollapsed + ? + : + )} - + {!isCollapsed && }
- + {!isCollapsed && } })} @@ -664,11 +674,13 @@ function ListHead({ type, node, makeEdit, ctx }: Props) { - const { locale } = useLocale() if (!JsonArrayNode.is(node)) { return <> } + const { locale } = useLocale() + const { expand, collapse, isToggled } = useToggles() + const type = node.typeDef && isListOrArray(node.typeDef) ? node.typeDef : outerType const canAdd = (type.lengthRange?.max ?? Infinity) > (node?.children?.length ?? 0) @@ -734,6 +746,9 @@ function ListBody({ type: outerType, node, makeEdit, ctx }: Props 20)) const makeItemEdit: MakeEdit = (edit) => { makeEdit(() => { const newChild = edit(child?.range ?? item.range) @@ -750,6 +765,10 @@ function ListBody({ type: outerType, node, makeEdit, ctx }: Props
+ {canToggle && (isCollapsed + ? + : + )} @@ -762,13 +781,13 @@ function ListBody({ type: outerType, node, makeEdit, ctx }: Props
} - + {!isCollapsed && } - {childType.kind === 'struct' + {!isCollapsed && (childType.kind === 'struct' ?
- : } + : )} })} {node.children.length > 0 &&
@@ -991,3 +1010,33 @@ function Docs({ desc }: DocsProps) { {desc}
} + +function useToggles() { + const [toggleState, setToggleState] = useState(new Map()) + const [toggleAll, setToggleAll] = useState(undefined) + + const expand = useCallback((key: string) => (evt: MouseEvent) => { + if (evt.ctrlKey) { + setToggleState(new Map()) + setToggleAll(true) + } else { + setToggleState(state => new Map(state.set(key, true))) + } + }, []) + + const collapse = useCallback((key: string) => (evt: MouseEvent) => { + if (evt.ctrlKey) { + setToggleState(new Map()) + setToggleAll(false) + } else { + setToggleState(state => new Map(state.set(key, false))) + } + }, []) + + const isToggled = useCallback((key: string) => { + if (!(toggleState instanceof Map)) return false + return toggleState.get(key) ?? toggleAll + }, [toggleState, toggleAll]) + + return { expand, collapse, isToggled } +}