soiz1's picture
Upload folder using huggingface_hub
4d70170 verified
raw
history blame
19.7 kB
<script lang="ts">
/* eslint-disable vue/no-unused-refs */
import { defineComponent, toRaw } from 'vue'
import {
BridgeEvents,
copyToClipboard,
isPlainObject,
openInEditor,
sortByKey,
} from '@vue-devtools/shared-utils'
import DataFieldEdit from '@front/mixins/data-field-edit'
import { formattedValue, valueDetails, valueType } from '@front/util/format'
import { getBridge } from '../bridge'
function subFieldCount(value) {
if (Array.isArray(value)) {
return value.length
}
else if (value && typeof value === 'object') {
return Object.keys(value).length
}
else {
return 0
}
}
export default defineComponent({
name: 'DataField',
mixins: [
DataFieldEdit,
],
props: {
field: {
type: Object,
required: true,
},
depth: {
type: Number,
required: true,
},
path: {
type: String,
required: true,
},
forceCollapse: {
type: String,
default: null,
},
isStateField: {
type: Boolean,
default: false,
},
},
emits: ['editState'],
data() {
return {
contextMenuOpen: false,
limit: 20,
expanded: false,
}
},
computed: {
depthMargin(): number {
return (this.depth + 1) * 14 + 10
},
valueType(): string {
return valueType(this.field.value)
},
interpretedValueType(): string {
return valueType(this.field.value, false)
},
valueDetails(): string {
return valueDetails(this.field.value)
},
nativeValueType(): string {
return typeof this.field.value
},
isExpandableType(): boolean {
let value = this.field.value
if (this.valueType === 'custom') {
value = value._custom.value
}
const closed = this.fieldOptions.closed
const closedDefined = typeof closed !== 'undefined'
return (!closedDefined
&& (
Array.isArray(value)
|| isPlainObject(value)
))
|| (
closedDefined
&& !closed
)
},
formattedValue(): string {
const value = this.field.value
const objectType = value?._custom?.objectType || this.field.objectType
if (objectType === 'Reactive') {
return 'Reactive'
}
else if (this.fieldOptions.abstract) {
return ''
}
else {
let result = `<span class="value-formatted-ouput">${formattedValue(value)}</span>`
if (objectType) {
result += ` <span class="text-gray-500">(${objectType})</span>`
}
return result
}
},
rawValue(): any {
let value = this.field.value
// CustomValue API
const isCustom = this.valueType === 'custom'
let inherit = {}
if (isCustom) {
inherit = value._custom.fields || {}
value = value._custom.value
}
if (value && value._isArray) {
value = value.items
}
return { value, inherit }
},
formattedSubFields(): any[] {
let { value, inherit } = this.rawValue
if (Array.isArray(value)) {
return value.slice(0, this.limit).map((item, i) => ({
key: i,
value: item,
...inherit,
}))
}
else if (typeof value === 'object') {
value = Object.keys(value).map(key => ({
key,
value: value[key],
...inherit,
}))
if (this.valueType !== 'custom') {
value = sortByKey(value)
}
}
return value.slice(0, this.limit)
},
subFieldCount(): number {
const { value } = this.rawValue
return subFieldCount(value)
},
valueTooltip(): string {
const type = this.valueType
if (this.field.raw) {
return `<span class="font-mono">${this.field.raw}</span>`
}
else if (type === 'custom') {
return this.field.value._custom.tooltip
}
else if (type.indexOf('native ') === 0) {
return type.substring('native '.length)
}
else {
return null
}
},
fieldOptions(): any {
if (this.valueType === 'custom') {
return Object.assign({}, this.field, this.field.value._custom)
}
else {
return this.field
}
},
editErrorMessage(): string {
if (!this.valueValid) {
return 'Invalid value (must be valid JSON)'
}
else if (!this.keyValid) {
if (this.duplicateKey) {
return 'Duplicate key'
}
else {
return 'Invalid key'
}
}
return ''
},
valueClass(): string[] {
const cssClass = [this.valueType, `raw-${this.nativeValueType}`]
if (this.valueType === 'custom') {
const value = this.field.value
value._custom.type && cssClass.push(`type-${value._custom.type}`)
value._custom.class && cssClass.push(value._custom.class)
}
return cssClass
},
displayedKey(): string {
let key = this.field.key
if (typeof key === 'string') {
key = key.replace('__vue__', '')
}
return key
},
customActions(): { icon: string, tooltip?: string }[] {
return this.field.value?._custom?.actions ?? []
},
},
watch: {
forceCollapse: {
handler(value) {
if (value === 'expand' && this.depth < 4) {
this.expanded = true
}
else if (value === 'collapse') {
this.expanded = false
}
},
immediate: true,
},
},
created() {
const value = this.field.value && this.field.value._custom ? this.field.value._custom.value : this.field.value
this.expanded = this.depth === 0 && this.field.key !== '$route' && (subFieldCount(value) < 12)
},
methods: {
copyValue() {
copyToClipboard(this.field.value)
},
copyPath() {
copyToClipboard(this.path)
},
onClick(event) {
// Cancel if target is interactive
if (event.target.tagName === 'INPUT' || event.target.className.includes('button')) {
return
}
// CustomValue API `file`
if (this.valueType === 'custom' && this.fieldOptions.file) {
return openInEditor(this.fieldOptions.file)
}
if (this.valueType === 'custom' && this.fieldOptions.type === '$refs') {
if (this.$isChrome) {
const evl = `inspect(window.__VUE_DEVTOOLS_INSTANCE_MAP__.get("${this.fieldOptions.uid}").$refs["${this.fieldOptions.key}"])`
chrome.devtools.inspectedWindow.eval(evl)
}
else {
// eslint-disable-next-line no-alert
window.alert('DOM inspection is not supported in this shell.')
}
}
// Default action
this.toggle()
},
toggle() {
if (this.isExpandableType) {
this.expanded = !this.expanded
!this.expanded && this.cancelCurrentEdition()
}
},
hyphen: v => v.replace(/\s/g, '-'),
onContextMenuMouseEnter() {
clearTimeout(this.$_contextMenuTimer)
},
onContextMenuMouseLeave() {
clearTimeout(this.$_contextMenuTimer)
this.$_contextMenuTimer = setTimeout(() => {
this.contextMenuOpen = false
}, 4000)
},
showMoreSubfields() {
this.limit += 20
},
logToConsole(level = 'log') {
getBridge().send(BridgeEvents.TO_BACK_LOG, {
level,
value: toRaw(this.field.value),
revive: true,
})
},
executeCustomAction(index: number) {
getBridge().send(BridgeEvents.TO_BACK_CUSTOM_STATE_ACTION, {
value: toRaw(this.field.value),
actionIndex: index,
})
},
},
renderError: null,
})
</script>
<template>
<div class="data-field">
<VTooltip
:style="{ marginLeft: `${depth * 14}px` }"
:disabled="!field.meta"
:class="{
'force-toolbar z-10': contextMenuOpen || editing,
}"
class="self"
placement="left"
:distance="0"
:skidding="24"
@click="onClick"
@mouseenter="onContextMenuMouseEnter"
@mouseleave="onContextMenuMouseLeave"
>
<span
v-show="isExpandableType"
:class="{ rotated: expanded }"
class="arrow right"
/>
<span
v-if="editing && renamable"
>
<input
ref="keyInput"
v-model="editedKey"
class="edit-input key-input"
:class="{ error: !keyValid }"
@keydown.esc.capture.stop.prevent="cancelEdit()"
@keydown.enter="submitEdit()"
>
</span>
<span
v-else
v-tooltip="fieldOptions.abstract && {
content: valueTooltip,
html: true,
}"
:class="{ abstract: fieldOptions.abstract }"
class="key text-purple-700 dark:text-purple-300"
>{{ displayedKey }}</span><span
v-if="!fieldOptions.abstract"
class="colon"
>:</span>
<span
v-if="editing"
class="edit-overlay"
>
<input
ref="editInput"
v-model="editedValue"
class="edit-input value-input text-black"
:class="{ error: !valueValid }"
list="special-tokens"
:type="inputType"
@keydown.esc.capture.stop.prevent="cancelEdit()"
@keydown.enter="submitEdit()"
>
<span class="actions">
<VueIcon
v-if="!editValid"
v-tooltip="editErrorMessage"
class="small-icon warning"
icon="warning"
/>
<template v-else>
<VueButton
v-tooltip="{
content: $t('DataField.edit.cancel.tooltip'),
html: true,
}"
class="icon-button flat"
icon-left="cancel"
@click="cancelEdit()"
/>
<VueButton
v-tooltip="{
content: $t('DataField.edit.submit.tooltip'),
html: true,
}"
class="icon-button flat"
icon-left="save"
@click="submitEdit()"
/>
</template>
</span>
</span>
<template v-else>
<!-- eslint-disable vue/no-v-html -->
<span
v-tooltip="{
content: valueTooltip,
html: true,
}"
:class="valueClass"
class="value"
@dblclick="openEdit()"
v-html="formattedValue"
/>
<!-- eslint-enable vue/no-v-html -->
<span class="actions">
<VueDropdown
v-if="valueDetails"
>
<template #trigger>
<VueButton
v-tooltip="'More details'"
class="edit-value icon-button flat"
icon-left="info"
/>
</template>
<template #default>
<div class="px-4 py-2 flex flex-col max-h-48 overflow-auto relative">
<div class="flex items-center fixed top-0 right-0">
<VueButton
v-close-popper
class="edit-value icon-button flat"
icon-left="close"
/>
</div>
<div class="whitespace-pre-wrap max-w-lg break-words text-xs font-mono">{{ valueDetails }}</div>
</div>
</template>
</VueDropdown>
<VueButton
v-for="(action, index) of customActions"
:key="index"
v-tooltip="action.tooltip"
class="icon-button flat"
:icon-left="action.icon"
@click="executeCustomAction(index)"
/>
<VueButton
v-if="valueType === 'native Error'"
v-tooltip="'Log error to console'"
class=" icon-button flat"
icon-left="input"
@click="logToConsole('error')"
/>
<VueButton
v-if="isValueEditable"
v-tooltip="'Edit value'"
class="edit-value icon-button flat"
icon-left="edit"
@click="openEdit()"
/>
<template v-if="quickEdits">
<VueButton
v-for="(info, index) of quickEdits"
:key="index"
v-tooltip="{
content: info.title || 'Quick edit',
html: true,
}"
:class="info.class"
:icon-left="info.icon"
class="quick-edit icon-button flat"
@click="quickEdit(info, $event)"
/>
</template>
<VueButton
v-if="isSubfieldsEditable && !addingValue"
v-tooltip="'Add new value'"
class="add-value icon-button flat"
icon-left="add_circle"
@click="addNewValue()"
/>
<VueButton
v-if="removable"
v-tooltip="'Remove value'"
class="remove-field icon-button flat"
icon-left="delete"
@click="removeField()"
/>
<!-- Context menu -->
<VueDropdown
v-model="contextMenuOpen"
>
<template #trigger>
<VueButton
icon-left="more_vert"
class="icon-button flat"
/>
</template>
<div
class="context-menu-dropdown"
@mouseenter="onContextMenuMouseEnter"
@mouseleave="onContextMenuMouseLeave"
>
<VueDropdownButton
icon-left="flip_to_front"
@click="copyValue"
>
{{ $t('DataField.contextMenu.copyValue') }}
</VueDropdownButton>
<VueDropdownButton
icon-left="flip_to_front"
@click="copyPath"
>
{{ $t('DataField.contextMenu.copyPath') }}
</VueDropdownButton>
</div>
</VueDropdown>
</span>
</template>
<template #popper>
<div
v-if="field.meta"
class="meta"
>
<div
v-for="(val, key) in field.meta"
:key="key"
class="meta-field"
>
<span class="key">{{ key }}</span>
<span class="value">{{ val }}</span>
</div>
</div>
</template>
</VTooltip>
<div
v-if="expanded && isExpandableType"
class="children"
>
<DataField
v-for="subField in formattedSubFields"
:key="subField.key"
:field="subField"
:parent-field="field"
:depth="depth + 1"
:path="`${path}.${subField.key}`"
:editable="isEditable"
:removable="isSubfieldsEditable"
:renamable="editable && valueType === 'plain-object'"
:force-collapse="forceCollapse"
:is-state-field="isStateField"
@edit-state="(path, payload) => $emit('editState', path, payload)"
/>
<VueButton
v-if="subFieldCount > limit"
v-tooltip="'Show more'"
:style="{ marginLeft: `${depthMargin}px` }"
icon-left="more_horiz"
class="icon-button flat more"
@click="showMoreSubfields()"
/>
<DataField
v-if="isSubfieldsEditable && addingValue"
ref="newField"
:field="newField"
:depth="depth + 1"
:path="`${path}.${newField.key}`"
:renamable="valueType === 'plain-object'"
:force-collapse="forceCollapse"
editable
removable
:is-state-field="isStateField"
@cancel-edit="addingValue = false"
@submit-edit="addingValue = false"
@edit-state="(path, payload) => $emit('editState', path, payload)"
/>
</div>
</div>
</template>
<style lang="stylus" scoped>
.data-field
user-select text
font-size 12px
@apply font-mono;
cursor pointer
.self
height 20px
line-height 20px
position relative
white-space nowrap
padding-left 14px
.high-density &
height 14px
line-height 14px
span, div
display inline-block
vertical-align middle
.arrow
position absolute
top 7px
left 0px
transition transform .1s ease
&.rotated
transform rotate(90deg)
.actions
display inline-flex
align-items center
position relative
top -1px
> *
visibility hidden
&:first-child
margin-left 6px
&:not(:last-child)
margin-right 6px
&.v-popper--shown
visibility visible
.icon-button
user-select none
width 20px
height @width
.icon-button :deep(.vue-ui-icon),
.small-icon
width 16px
height @width
.warning :deep(svg)
fill $orange
&:hover,
&.force-toolbar
.actions > *
visibility visible
.colon
margin-right .5em
position relative
.type
color $background-color
padding 3px 6px
font-size 10px
line-height 10px
height 16px
border-radius 3px
margin 2px 6px
position relative
background-color #eee
&.prop
background-color #96afdd
&.computed
background-color #af90d5
&.vuex-getter
background-color #5dd5d5
&.firebase-binding
background-color #ffcc00
&.observable
background-color #ff9999
.vue-ui-dark-mode &
color: #242424
.edit-overlay
display inline-flex
align-items center
.key
&.abstract
color $blueishGrey
.vue-ui-dark-mode &
color lighten($blueishGrey, 20%)
.value
display inline-block
color #444
&.string, &.native
color $red
&.string
:deep(span)
color $black
.vue-ui-dark-mode &
color $red
&.null
color #999
&.literal
color $vividBlue
&.raw-boolean :deep(.value-formatted-ouput)
width 36px
display inline-block
&.native.Error
background $red
color $white !important
padding 0 4px
border-radius $br
&::before
content 'Error: '
opacity .75
&.custom
&.type-component
color $green
&::before,
&::after
color $darkGrey
&::before
content '<'
&::after
content '>'
&.type-function
font-style italic
:deep(span)
color $vividBlue
font-family dejavu sans mono, monospace
.platform-mac &
font-family Menlo, monospace
.platform-windows &
font-family Consolas, Lucida Console, Courier New, monospace
.vue-ui-dark-mode &
color $purple
&.type-component-definition
color $green
:deep(span)
color $darkerGrey
&.type-reference
opacity 0.5
:deep(.attr-title)
color #800080
.vue-ui-dark-mode &
color #e36eec
.vue-ui-dark-mode &
color #bdc6cf
&.string, &.native
color #e33e3a
&.null
color #999
&.literal
color $purple
.meta
font-size 12px
font-family Menlo, Consolas, monospace
min-width 150px
.key
display inline-block
width 80px
color lighten(#881391, 60%)
.vue-ui-dark-mode &
color #881391
.value
color white
.vue-ui-dark-mode &
color black
.meta-field
&:not(:last-child)
margin-bottom 4px
.edit-input
font-family Menlo, Consolas, monospace
border solid 1px $green
border-radius 3px
padding 2px
outline none
&.error
border-color $orange
.value-input
width 180px
.key-input
width 90px
color #881391
.remove-field
margin-left 10px
.context-menu-dropdown
.vue-ui-button
display block
width 100%
.more
width 20px
height @width
:deep(.vue-ui-icon)
width 16px
height @width
</style>