Skip to content

feat: shift + click to expand all child components #1752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 24, 2022
1 change: 1 addition & 0 deletions packages/api/src/api/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface ComponentTreeNode {
isRouterView?: boolean
macthedRouteSegment?: string
tags: InspectorNodeTag[]
autoOpen: boolean
meta?: any
}

Expand Down
1 change: 1 addition & 0 deletions packages/api/src/api/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type HookPayloads = {
componentTreeData: ComponentTreeNode[]
maxDepth: number
filter: string
recursively: boolean
}
[Hooks.VISIT_COMPONENT_TREE]: {
app: App
Expand Down
3 changes: 2 additions & 1 deletion packages/app-backend-api/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
6 changes: 3 additions & 3 deletions packages/app-backend-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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),
Expand Down Expand Up @@ -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) {
Expand Down
12 changes: 6 additions & 6 deletions packages/app-backend-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
})
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/app-backend-core/src/perf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}
}
Expand Down
6 changes: 5 additions & 1 deletion packages/app-backend-vue2/src/components/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ComponentTreeNode[]> {
export async function walkTree (instance, pFilter: string, pRecursively: boolean, api: DevtoolsApi, ctx: BackendContext): Promise<ComponentTreeNode[]> {
initCtx(api, ctx)
filter = pFilter
recursively = pRecursively
functionalIds.clear()
captureIds.clear()
const result: ComponentTreeNode[] = flatten(await findQualifiedChildren(instance))
Expand Down Expand Up @@ -215,6 +217,7 @@ async function capture (instance, index?: number, list?: any[]): Promise<Compone
hasChildren: !!children.length,
inactive: false,
isFragment: false, // TODO: Check what is it for.
autoOpen: recursively,
}
return api.visitComponentTree(
instance,
Expand Down Expand Up @@ -251,6 +254,7 @@ async function capture (instance, index?: number, list?: any[]): Promise<Compone
isFragment: !!instance._isFragment,
children,
hasChildren: !!children.length,
autoOpen: recursively,
tags: [],
meta: {},
}
Expand Down
2 changes: 1 addition & 1 deletion packages/app-backend-vue2/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const backend = defineBackend({
})

api.on.walkComponentTree(async (payload, ctx) => {
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) => {
Expand Down
5 changes: 4 additions & 1 deletion packages/app-backend-vue3/src/components/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, undefined>

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)
}

Expand Down Expand Up @@ -161,6 +163,7 @@ export class ComponentWalker {
backgroundColor: 0xeeeeee,
},
],
autoOpen: this.recursively,
}

// capture children
Expand Down
4 changes: 2 additions & 2 deletions packages/app-backend-vue3/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand All @@ -157,7 +164,7 @@ export default defineComponent({
selected,
select,
expanded,
toggle,
switchToggle,
highlight,
unhighlight,
selectNextSibling,
Expand Down Expand Up @@ -194,7 +201,7 @@ export default defineComponent({
:class="{
'invisible': !instance.hasChildren
}"
@click.stop="toggle()"
@click.stop="switchToggle"
>
<span
:class="{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,21 @@ export function useComponent (instance: Ref<ComponentTreeNode>) {
const isExpanded = computed(() => isComponentOpen(instance.value.id))
const isExpandedUndefined = computed(() => expandedMap.value[instance.value.id] == null)

function toggleExpand () {
if (!instance.value.hasChildren) return
setComponentOpen(instance.value.id, !isExpanded.value)
if (isComponentOpen(instance.value.id)) {
requestComponentTree(instance.value.id)
function toggleExpand (recursively = false, value?, child?) {
const treeNode = child || instance.value
if (!treeNode.hasChildren) return
const isOpen = value === undefined ? !isExpanded.value : value
setComponentOpen(treeNode.id, isOpen)
if (isComponentOpen(treeNode.id)) {
requestComponentTree(treeNode.id, recursively)
} else {
// stop expanding all treenode
treeNode.autoOpen = false
}
if (recursively) {
treeNode.children.forEach(child => {
toggleExpand(recursively, value, child)
})
}
}

Expand Down Expand Up @@ -168,7 +178,9 @@ export function useComponent (instance: Ref<ComponentTreeNode>) {
}

onMounted(() => {
if (isExpanded.value) {
if (instance.value.autoOpen) {
toggleExpand(true, true)
} else if (isExpanded.value) {
requestComponentTree(instance.value.id)
}
})
Expand Down Expand Up @@ -266,7 +278,7 @@ export const requestedComponentTree = new Set()

let requestComponentTreeRetryDelay = 500

export async function requestComponentTree (instanceId: ComponentTreeNode['id'] = null) {
export async function requestComponentTree (instanceId: ComponentTreeNode['id'] = null, recursively = false) {
if (!instanceId) {
instanceId = '_root'
}
Expand All @@ -278,29 +290,30 @@ export async function requestComponentTree (instanceId: ComponentTreeNode['id']

await waitForAppSelect()

_sendTreeRequest(instanceId)
_queueRetryTree(instanceId)
_sendTreeRequest(instanceId, recursively)
_queueRetryTree(instanceId, recursively)
}

function _sendTreeRequest (instanceId: ComponentTreeNode['id']) {
function _sendTreeRequest (instanceId: ComponentTreeNode['id'], recursively = false) {
getBridge().send(BridgeEvents.TO_BACK_COMPONENT_TREE, {
instanceId,
filter: treeFilter.value,
recursively,
})
}

function _queueRetryTree (instanceId: ComponentTreeNode['id']) {
setTimeout(() => _retryRequestComponentTree(instanceId), requestComponentTreeRetryDelay)
function _queueRetryTree (instanceId: ComponentTreeNode['id'], recursively = false) {
setTimeout(() => _retryRequestComponentTree(instanceId, recursively), requestComponentTreeRetryDelay)
requestComponentTreeRetryDelay *= 1.5
}

function _retryRequestComponentTree (instanceId: ComponentTreeNode['id']) {
function _retryRequestComponentTree (instanceId: ComponentTreeNode['id'], recursively = false) {
if (rootInstances.value.length) {
requestComponentTreeRetryDelay = 500
return
}
_sendTreeRequest(instanceId)
_queueRetryTree(instanceId)
_sendTreeRequest(instanceId, recursively)
_queueRetryTree(instanceId, recursively)
}

export function ensureComponentsMapData (data: ComponentTreeNode) {
Expand Down