soiz1's picture
Upload folder using huggingface_hub
4d70170 verified
raw
history blame
9.44 kB
<script lang="ts">
import type { PropType } from 'vue'
import { computed, defineComponent, onMounted, ref, toRefs, watch } from 'vue'
import type { ComponentTreeNode } from '@vue/devtools-api'
import scrollIntoView from 'scroll-into-view-if-needed'
import { SharedData, UNDEFINED, getComponentDisplayName } from '@vue-devtools/shared-utils'
import { onKeyDown } from '@front/util/keyboard'
import { reactiveNow, useTimeAgo } from '@front/util/time'
import { sortChildren, updateTrackingEvents, updateTrackingLimit, useComponent, useComponentHighlight } from './composable'
const DEFAULT_EXPAND_DEPTH = 2
export default defineComponent({
name: 'ComponentTreeNode',
props: {
instance: {
type: Object as PropType<ComponentTreeNode>,
required: true,
},
depth: {
type: Number,
default: 0,
},
},
emits: ['selectNextSibling', 'selectPreviousSibling'],
setup(props, { emit }) {
const { instance } = toRefs(props)
const displayName = computed(() => getComponentDisplayName(props.instance.name, SharedData.componentNameStyle))
const componentHasKey = computed(() => (props.instance.renderKey === 0 || !!props.instance.renderKey) && props.instance.renderKey !== UNDEFINED)
const sortedChildren = computed<ComponentTreeNode[]>(() => props.instance.children
? sortChildren(props.instance.children)
: [])
const {
isSelected: selected,
select,
isExpanded: expanded,
isExpandedUndefined,
isComponentOpen,
toggleExpand: toggle,
subscribeToComponentTree,
} = useComponent(instance)
subscribeToComponentTree()
const toggleEl = ref<HTMLElement>(null)
onMounted(() => {
if (isExpandedUndefined.value && props.depth < DEFAULT_EXPAND_DEPTH) {
toggle()
}
})
// Highlight
const { highlight, unhighlight } = useComponentHighlight(computed(() => props.instance.id))
// Auto scroll
function autoScroll() {
if (selected.value && toggleEl.value) {
/** @type {HTMLElement} */
const el = toggleEl.value
scrollIntoView(el, {
scrollMode: 'if-needed',
block: 'center',
inline: 'nearest',
behavior: 'smooth',
})
}
}
watch(selected, () => autoScroll())
watch(toggleEl, () => autoScroll())
// Keyboard
onKeyDown((event) => {
if (selected.value) {
requestAnimationFrame(() => {
switch (event.key) {
case 'ArrowRight': {
if (!expanded.value) {
toggle()
}
break
}
case 'ArrowLeft': {
if (expanded.value) {
toggle()
}
break
}
case ' ':
case 'Enter': {
toggle()
break
}
case 'ArrowDown': {
if (expanded.value && sortedChildren.value.length) {
// Select first child
select(sortedChildren.value[0].id)
}
else {
emit('selectNextSibling')
}
break
}
case 'ArrowUp': {
emit('selectPreviousSibling')
}
}
})
}
})
function selectNextSibling(index) {
if (index + 1 >= sortedChildren.value.length) {
emit('selectNextSibling')
}
else {
select(sortedChildren.value[index + 1].id)
}
}
function selectPreviousSibling(index) {
if (index === 0 || !sortedChildren.value.length) {
if (selected.value) {
emit('selectPreviousSibling')
}
else {
select()
}
}
else {
let child = sortedChildren.value[index - 1]
while (child) {
if (child.children.length && isComponentOpen(child.id)) {
const children = sortChildren(child.children)
child = children[children.length - 1]
}
else {
select(child.id)
child = null
}
}
}
}
function switchToggle(event: MouseEvent) {
if (event.shiftKey) {
toggle(true, !expanded.value)
}
else {
toggle()
}
}
// Update tracking
const updateTracking = computed(() => updateTrackingEvents.value[props.instance.id])
const showUpdateTracking = computed(() => updateTracking.value?.time > updateTrackingLimit.value && updateTracking.value?.time > reactiveNow.value - 20_000)
const updateTrackingTime = computed(() => updateTracking.value?.time)
const { timeAgo: updateTrackingTimeAgo } = useTimeAgo(updateTrackingTime)
const updateTrackingOpacity = computed(() => showUpdateTracking.value ? 1 - (reactiveNow.value - updateTracking.value?.time) / 20_000 : 0)
return {
toggleEl,
sortedChildren,
displayName,
componentHasKey,
selected,
select,
expanded,
switchToggle,
highlight,
unhighlight,
selectNextSibling,
selectPreviousSibling,
showUpdateTracking,
updateTracking,
updateTrackingTimeAgo,
updateTrackingOpacity,
}
},
})
</script>
<template>
<div class="min-w-max">
<div
ref="toggleEl"
class="font-mono cursor-pointer relative z-10 rounded whitespace-nowrap flex items-center pr-2 text-sm select-none selectable-item"
:class="{
selected,
'opacity-50': instance.inactive,
}"
:style="{
paddingLeft: `${depth * 15 + 4}px`,
}"
@click="select()"
@dblclick="switchToggle"
@mouseover="highlight()"
@mouseleave="unhighlight()"
>
<!-- arrow wrapper for better hit box -->
<span
class="w-4 h-4 flex items-center justify-center"
:class="{
invisible: !instance.hasChildren,
}"
@click.stop="switchToggle"
>
<span
:class="{
'transform rotate-90': expanded,
}"
class="arrow right"
/>
</span>
<!-- Component tag -->
<span class="content">
<span
class="angle-bracket"
:class="[
selected ? 'text-white/60' : 'text-gray-400 dark:text-gray-600',
]"
>&lt;</span>
<span class="item-name text-green-500">{{ displayName }}</span>
<span
v-if="componentHasKey"
class="opacity-50 text-xs"
:class="{
'opacity-100': selected,
}"
>
<span
:class="{
'text-purple-500': !selected,
'text-purple-200': selected,
}"
> key</span>=<span>{{ instance.renderKey }}</span>
</span>
<span
class="angle-bracket"
:class="[
selected ? 'text-white/60' : 'text-gray-400 dark:text-gray-600',
]"
>&gt;</span>
</span>
<span class="flex items-center space-x-2 ml-2 h-full">
<span
v-if="instance.isFragment"
v-tooltip="'Has multiple root DOM nodes'"
class="info fragment bg-blue-400 dark:bg-blue-800"
>
fragment
</span>
<span
v-if="instance.inactive"
v-tooltip="'Currently inactive but not destroyed'"
class="info inactive bg-gray-500"
>
inactive
</span>
<span
v-for="(tag, index) of instance.tags"
:key="index"
v-tooltip="{
content: tag.tooltip,
html: true,
}"
:style="{
color: `#${tag.textColor.toString(16).padStart(6, '0')}`,
backgroundColor: `#${tag.backgroundColor.toString(16).padStart(6, '0')}`,
}"
class="info tag rounded-sm"
>
{{ tag.label }}
</span>
<template v-if="$shared.debugInfo">
<span
v-tooltip="'id'"
class="info bg-gray-500"
>
{{ instance.id }}
</span>
<span
v-tooltip="'Order in DOM'"
class="info bg-gray-500"
>
{{ instance.domOrder }}
</span>
</template>
<VTooltip
v-if="showUpdateTracking"
class="h-4"
>
<div class="px-3 -mx-2 h-full flex items-center">
<div
class="w-1 h-1 rounded-full"
:class="[
selected ? 'bg-white' : 'bg-green-500',
]"
:style="{
opacity: updateTrackingOpacity,
}"
/>
</div>
<template #popper>
<div>
Updated {{ updateTrackingTimeAgo }}
</div>
</template>
</VTooltip>
</span>
</div>
<div v-if="expanded && instance.children">
<ComponentTreeNode
v-for="(child, index) in sortedChildren"
:key="child.id"
:instance="child"
:depth="depth + 1"
@select-next-sibling="selectNextSibling(index)"
@select-previous-sibling="selectPreviousSibling(index)"
/>
</div>
</div>
</template>
<style lang="stylus" scoped>
.info
color #fff
font-size 10px
padding 3px 5px 2px
display inline-block
line-height 10px
border-radius 3px
</style>