Skip to content

feat: highlighting component boundary #882

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ onRpcConnected(() => {
rpc.value.emit('update-client-state', {
minimizePanelInteractive: devtoolsClientState.value.minimizePanelInteractive,
closeOnOutsideClick: devtoolsClientState.value.interactionCloseOnOutsideClick,
highlightComponentTracking: devtoolsClientState.value.highlightComponentTracking,
showFloatingPanel: devtoolsClientState.value.showPanel,
reduceMotion: devtoolsClientState.value.reduceMotion,
})
Expand Down
2 changes: 2 additions & 0 deletions packages/client/src/composables/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface DevtoolsClientState {
scale: number
interactionCloseOnOutsideClick: boolean
showPanel: boolean
highlightComponentTracking: boolean
minimizePanelInteractive: number
reduceMotion: boolean
}
Expand Down Expand Up @@ -45,6 +46,7 @@ function clientStateFactory(): DevtoolsClientState {
scale: 1,
interactionCloseOnOutsideClick: false,
showPanel: true,
highlightComponentTracking: false,
minimizePanelInteractive: 5000,
reduceMotion: false,
}
Expand Down
8 changes: 7 additions & 1 deletion packages/client/src/pages/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const hostEnv = useHostEnv()
*/
const enableFeatureSettings = hostEnv === 'iframe' || hostEnv === 'separate-window'

const { scale, interactionCloseOnOutsideClick, showPanel, minimizePanelInteractive, expandSidebar, scrollableSidebar, reduceMotion } = toRefs(toReactive(devtoolsClientState))
const { scale, interactionCloseOnOutsideClick, showPanel, minimizePanelInteractive, expandSidebar, scrollableSidebar, reduceMotion, highlightComponentTracking } = toRefs(toReactive(devtoolsClientState))

// #region settings
const scaleOptions = [
Expand Down Expand Up @@ -210,6 +210,12 @@ const minimizePanelInteractiveLabel = computed(() => {
<div>
<VueSelect v-model="minimizePanelInteractive" :button-props="{ outlined: true }" :options="minimizePanelInteractiveOptions" :placeholder="minimizePanelInteractiveLabel" />
</div>
<div mx--2 my1 h-1px border="b base" op75 />

<div class="flex items-center gap2 text-sm">
<VueCheckbox v-model="highlightComponentTracking" />
<span op75>Highlight Component Re Rendering</span>
</div>
</VueCard>
</template>

Expand Down
61 changes: 61 additions & 0 deletions packages/devtools-kit/src/core/component-flash/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ComponentHighLighterOptions, VueAppInstance } from '../../../types'
import { getComponentBoundingRect } from '../component/state/bounding-rect'
import { getInstanceName } from '../component/utils'

const CONTAINER_ELEMENT_ID = '__vue-devtools-flash__'
const REMOVE_DELAY_MS = 1000

const containerStyles = {
border: '2px rgba(65, 184, 131, 0.7) solid',
position: 'fixed',
zIndex: '2147483645',
pointerEvents: 'none',
borderRadius: '3px',
boxSizing: 'border-box',
transition: 'none',
opacity: '1',
}

function getStyles(bounds: ComponentHighLighterOptions['bounds']) {
return {
left: `${Math.round(bounds.left)}px`,
top: `${Math.round(bounds.top)}px`,
width: `${Math.round(bounds.width)}px`,
height: `${Math.round(bounds.height)}px`,
} satisfies Partial<CSSStyleDeclaration>
}

function create(options: ComponentHighLighterOptions & { elementId?: string, style?: Partial<CSSStyleDeclaration> }) {
const containerEl = document.createElement('div')
containerEl.id = options?.elementId ?? CONTAINER_ELEMENT_ID

Object.assign(containerEl.style, {
...containerStyles,
...getStyles(options.bounds),
...options.style,
})

document.body.appendChild(containerEl)

requestAnimationFrame(() => {
containerEl.style.transition = 'opacity 1s'
containerEl.style.opacity = '0'
})

clearTimeout((containerEl as any)?._timer);
(containerEl as any)._timer = setTimeout(() => {
document.body.removeChild(containerEl)
}, REMOVE_DELAY_MS)

return containerEl
}

export function flashComponent(instance: VueAppInstance) {
const bounds = getComponentBoundingRect(instance)

if (!bounds.width && !bounds.height)
return

const name = getInstanceName(instance)
create({ bounds, name })
}
13 changes: 13 additions & 0 deletions packages/devtools-kit/src/core/plugin/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ComponentWalker } from '../../core/component/tree/walker'
import { getAppRecord, getComponentId, getComponentInstance } from '../../core/component/utils'
import { activeAppRecord, devtoolsContext, devtoolsState, DevToolsV6PluginAPIHookKeys } from '../../ctx'
import { hook } from '../../hook'
import { flashComponent } from '../component-flash'
import { setupBuiltinTimelineLayers } from '../timeline'
import { exposeInstanceToWindow } from '../vm'

Expand Down Expand Up @@ -115,6 +116,10 @@ export function createComponentsDevToolsPlugin(app: App): [PluginDescriptor, Plu
}
}

if (devtoolsState.flashUpdates) {
flashComponent(component)
}

if (!appRecord)
return

Expand Down Expand Up @@ -150,6 +155,10 @@ export function createComponentsDevToolsPlugin(app: App): [PluginDescriptor, Plu
}
}

if (devtoolsState.flashUpdates) {
flashComponent(component)
}

if (!appRecord)
return

Expand All @@ -166,6 +175,10 @@ export function createComponentsDevToolsPlugin(app: App): [PluginDescriptor, Plu
if (!app || (typeof uid !== 'number' && !uid) || !component)
return

if (devtoolsState.flashUpdates) {
flashComponent(component)
}

const appRecord = await getAppRecord(app)

if (!appRecord)
Expand Down
2 changes: 2 additions & 0 deletions packages/devtools-kit/src/ctx/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface DevToolsState {
tabs: CustomTab[]
commands: CustomCommand[]
highPerfModeEnabled: boolean
flashUpdates: boolean
devtoolsClientDetected: {
[key: string]: boolean
}
Expand All @@ -40,6 +41,7 @@ function initStateFactory() {
tabs: [],
commands: [],
highPerfModeEnabled: true,
flashUpdates: true,
devtoolsClientDetected: {},
perfUniqueGroupId: 0,
timelineLayersState: getTimelineLayersStateFromStorage(),
Expand Down
1 change: 1 addition & 0 deletions packages/overlay/src/components/FrameBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ onRpcSeverReady(() => {
updateState({
minimizePanelInactive: v.minimizePanelInteractive,
closeOnOutsideClick: v.closeOnOutsideClick,
highlightComponentTracking: v.highlightComponentTracking,
preferShowFloatingPanel: v.showFloatingPanel,
reduceMotion: v.reduceMotion,
})
Expand Down
2 changes: 2 additions & 0 deletions packages/overlay/src/composables/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface DevToolsFrameState {
minimizePanelInactive: number
preferShowFloatingPanel: boolean
reduceMotion: boolean
highlightComponentTracking: boolean
}

export interface UseFrameStateReturn {
Expand All @@ -35,6 +36,7 @@ const state = useLocalStorage<DevToolsFrameState>('__vue-devtools-frame-state__'
minimizePanelInactive: 5000,
preferShowFloatingPanel: true,
reduceMotion: false,
highlightComponentTracking: false,
})

export function useFrameState(): UseFrameStateReturn {
Expand Down