diff --git a/packages/api/src/api/component.ts b/packages/api/src/api/component.ts index 5e545bf0f..d701fd21f 100644 --- a/packages/api/src/api/component.ts +++ b/packages/api/src/api/component.ts @@ -17,6 +17,7 @@ export interface ComponentTreeNode { isRouterView?: boolean macthedRouteSegment?: string tags: InspectorNodeTag[] + autoOpen: boolean meta?: any } diff --git a/packages/api/src/api/hooks.ts b/packages/api/src/api/hooks.ts index dededdc1a..98e549417 100644 --- a/packages/api/src/api/hooks.ts +++ b/packages/api/src/api/hooks.ts @@ -56,6 +56,7 @@ export type HookPayloads = { componentTreeData: ComponentTreeNode[] maxDepth: number filter: string + recursively: boolean } [Hooks.VISIT_COMPONENT_TREE]: { app: App diff --git a/packages/app-backend-api/src/api.ts b/packages/app-backend-api/src/api.ts index bc2b86bd2..51235fbda 100644 --- a/packages/app-backend-api/src/api.ts +++ b/packages/app-backend-api/src/api.ts @@ -88,12 +88,13 @@ export class DevtoolsApi { }) } - async walkComponentTree (instance: ComponentInstance, maxDepth = -1, filter: string = null) { + async walkComponentTree (instance: ComponentInstance, maxDepth = -1, filter: string = null, recursively = false) { const payload = await this.callHook(Hooks.WALK_COMPONENT_TREE, { componentInstance: instance, componentTreeData: null, maxDepth, filter, + recursively, }) return payload.componentTreeData } diff --git a/packages/app-backend-core/src/component.ts b/packages/app-backend-core/src/component.ts index 90b0aeba3..e402b5bb2 100644 --- a/packages/app-backend-core/src/component.ts +++ b/packages/app-backend-core/src/component.ts @@ -6,7 +6,7 @@ import { App, ComponentInstance, EditStatePayload, now } from '@vue/devtools-api const MAX_$VM = 10 const $vmQueue = [] -export async function sendComponentTreeData (appRecord: AppRecord, instanceId: string, filter = '', maxDepth: number = null, ctx: BackendContext) { +export async function sendComponentTreeData (appRecord: AppRecord, instanceId: string, filter = '', maxDepth: number = null, recursively = false, ctx: BackendContext) { if (!instanceId || appRecord !== ctx.currentAppRecord) return // Flush will send all components in the tree @@ -30,7 +30,7 @@ export async function sendComponentTreeData (appRecord: AppRecord, instanceId: s if (maxDepth == null) { maxDepth = instance === ctx.currentAppRecord.rootInstance ? 2 : 1 } - const data = await appRecord.backend.api.walkComponentTree(instance, maxDepth, filter) + const data = await appRecord.backend.api.walkComponentTree(instance, maxDepth, filter, recursively) const payload = { instanceId, treeData: stringify(data), @@ -128,7 +128,7 @@ export function getComponentInstance (appRecord: AppRecord, instanceId: string, export async function refreshComponentTreeSearch (ctx: BackendContext) { if (!ctx.currentAppRecord.componentFilter) return - await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, ctx) + await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, false, ctx) } export async function sendComponentUpdateTracking (instanceId: string, ctx: BackendContext) { diff --git a/packages/app-backend-core/src/index.ts b/packages/app-backend-core/src/index.ts index c0bacc1a7..48ca67001 100644 --- a/packages/app-backend-core/src/index.ts +++ b/packages/app-backend-core/src/index.ts @@ -125,7 +125,7 @@ async function connect () { // Update tree (tags) if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, sub => sub.payload.instanceId === id)) { - await sendComponentTreeData(appRecord, id, appRecord.componentFilter, 0, ctx) + await sendComponentTreeData(appRecord, id, appRecord.componentFilter, 0, false, ctx) } } catch (e) { if (SharedData.debugInfo) { @@ -185,7 +185,7 @@ async function connect () { const parentId = await getComponentId(app, parentUid, parentInstances[i], ctx) if (i < 2 && isSubscribed(BridgeSubscriptions.COMPONENT_TREE, sub => sub.payload.instanceId === parentId)) { raf(() => { - sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, ctx) + sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx) }) } @@ -227,7 +227,7 @@ async function connect () { if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, sub => sub.payload.instanceId === parentId)) { raf(async () => { try { - sendComponentTreeData(await getAppRecord(app, ctx), parentId, appRecord.componentFilter, null, ctx) + sendComponentTreeData(await getAppRecord(app, ctx), parentId, appRecord.componentFilter, null, false, ctx) } catch (e) { if (SharedData.debugInfo) { console.error(e) @@ -365,7 +365,7 @@ async function connect () { const handleFlush = debounce(async () => { if (ctx.currentAppRecord?.backend.options.features.includes(BuiltinBackendFeature.FLUSH)) { - await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, ctx) + await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, false, ctx) if (ctx.currentInspectedComponentId) { await sendSelectedComponentData(ctx.currentAppRecord, ctx.currentInspectedComponentId, ctx) } @@ -433,10 +433,10 @@ function connectBridge () { // Components - ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_TREE, async ({ instanceId, filter }) => { + ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_TREE, async ({ instanceId, filter, recursively }) => { ctx.currentAppRecord.componentFilter = filter subscribe(BridgeSubscriptions.COMPONENT_TREE, { instanceId }) - await sendComponentTreeData(ctx.currentAppRecord, instanceId, filter, null, ctx) + await sendComponentTreeData(ctx.currentAppRecord, instanceId, filter, null, recursively, ctx) }) ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_SELECTED_DATA, async (instanceId) => { diff --git a/packages/app-backend-core/src/perf.ts b/packages/app-backend-core/src/perf.ts index db5697980..0cfa6193d 100644 --- a/packages/app-backend-core/src/perf.ts +++ b/packages/app-backend-core/src/perf.ts @@ -145,7 +145,7 @@ export async function performanceMarkEnd ( const id = await getComponentId(app, uid, instance, ctx) if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, sub => sub.payload.instanceId === id)) { raf(() => { - sendComponentTreeData(appRecord, id, ctx.currentAppRecord.componentFilter, null, ctx) + sendComponentTreeData(appRecord, id, ctx.currentAppRecord.componentFilter, null, false, ctx) }) } } diff --git a/packages/app-backend-vue2/src/components/tree.ts b/packages/app-backend-vue2/src/components/tree.ts index 435c1879e..fa7d23d90 100644 --- a/packages/app-backend-vue2/src/components/tree.ts +++ b/packages/app-backend-vue2/src/components/tree.ts @@ -15,15 +15,17 @@ let api: DevtoolsApi const consoleBoundInstances = Array(5) let filter = '' +let recursively = false const functionalIds = new Map() // Dedupe instances // Some instances may be both on a component and on a child abstract/functional component const captureIds = new Map() -export async function walkTree (instance, pFilter: string, api: DevtoolsApi, ctx: BackendContext): Promise { +export async function walkTree (instance, pFilter: string, pRecursively: boolean, api: DevtoolsApi, ctx: BackendContext): Promise { initCtx(api, ctx) filter = pFilter + recursively = pRecursively functionalIds.clear() captureIds.clear() const result: ComponentTreeNode[] = flatten(await findQualifiedChildren(instance)) @@ -215,6 +217,7 @@ async function capture (instance, index?: number, list?: any[]): Promise { - payload.componentTreeData = await walkTree(payload.componentInstance, payload.filter, api, ctx) + payload.componentTreeData = await walkTree(payload.componentInstance, payload.filter, payload.recursively, api, ctx) }) api.on.walkComponentParents((payload, ctx) => { diff --git a/packages/app-backend-vue3/src/components/tree.ts b/packages/app-backend-vue3/src/components/tree.ts index a60e114ce..392a6a12f 100644 --- a/packages/app-backend-vue3/src/components/tree.ts +++ b/packages/app-backend-vue3/src/components/tree.ts @@ -8,15 +8,17 @@ export class ComponentWalker { ctx: BackendContext api: DevtoolsApi maxDepth: number + recursively: boolean componentFilter: ComponentFilter // Dedupe instances // Some instances may be both on a component and on a child abstract/functional component captureIds: Map - constructor (maxDepth: number, filter: string, api: DevtoolsApi, ctx: BackendContext) { + constructor (maxDepth: number, filter: string, recursively: boolean, api: DevtoolsApi, ctx: BackendContext) { this.ctx = ctx this.api = api this.maxDepth = maxDepth + this.recursively = recursively this.componentFilter = new ComponentFilter(filter) } @@ -161,6 +163,7 @@ export class ComponentWalker { backgroundColor: 0xeeeeee, }, ], + autoOpen: this.recursively, } // capture children diff --git a/packages/app-backend-vue3/src/index.ts b/packages/app-backend-vue3/src/index.ts index 727cc0e1b..cfdf4c004 100644 --- a/packages/app-backend-vue3/src/index.ts +++ b/packages/app-backend-vue3/src/index.ts @@ -26,12 +26,12 @@ export const backend = defineBackend({ }) api.on.walkComponentTree(async (payload, ctx) => { - const walker = new ComponentWalker(payload.maxDepth, payload.filter, api, ctx) + const walker = new ComponentWalker(payload.maxDepth, payload.filter, payload.recursively, api, ctx) payload.componentTreeData = await walker.getComponentTree(payload.componentInstance) }) api.on.walkComponentParents((payload, ctx) => { - const walker = new ComponentWalker(0, null, api, ctx) + const walker = new ComponentWalker(0, null, false, api, ctx) payload.parentInstances = walker.getComponentParents(payload.componentInstance) }) diff --git a/packages/app-frontend/src/features/components/ComponentTreeNode.vue b/packages/app-frontend/src/features/components/ComponentTreeNode.vue index ae297a849..01ddfbbfa 100644 --- a/packages/app-frontend/src/features/components/ComponentTreeNode.vue +++ b/packages/app-frontend/src/features/components/ComponentTreeNode.vue @@ -141,6 +141,13 @@ export default defineComponent({ } } + function switchToggle (event: MouseEvent) { + if (event.shiftKey) { + toggle(true, !expanded.value) + } else { + toggle() + } + } // Update tracking const updateTracking = computed(() => updateTrackingEvents.value[props.instance.id]) @@ -157,7 +164,7 @@ export default defineComponent({ selected, select, expanded, - toggle, + switchToggle, highlight, unhighlight, selectNextSibling, @@ -194,7 +201,7 @@ export default defineComponent({ :class="{ 'invisible': !instance.hasChildren }" - @click.stop="toggle()" + @click.stop="switchToggle" >