Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { toRaw } from 'vue'
import { VueButton, VueDropdown, VueDropdownButton, VueIcon, VTooltip as vTooltip } from '@vue/devtools-ui'
import { getRawValue } from '@vue/devtools-kit'
import { getRaw } from '@vue/devtools-kit'
import type { InspectorState, InspectorStateEditorPayload } from '@vue/devtools-kit'
import type { ButtonProps } from '@vue/devtools-ui/dist/types/src/components/Button'
import { useDevToolsBridgeRpc } from '@vue/devtools-core'
Expand Down Expand Up @@ -30,7 +30,9 @@ const { copy, isSupported } = useClipboard()

const popupVisible = ref(false)

const rawValue = computed(() => getRawValue(props.data.value).value)
const raw = computed(() => getRaw(props.data.value))
const rawValue = computed(() => raw.value.value)
const customType = computed(() => raw.value.customType)
const dataType = computed(() => typeof rawValue.value)

const iconButtonProps = {
Expand All @@ -56,6 +58,13 @@ function quickEdit(v: unknown, remove: boolean = false) {
},
} satisfies InspectorStateEditorPayload)
}

function quickEditNum(v: number | string, offset: 1 | -1) {
const target = typeof v === 'number'
? v + offset
: BigInt(v) + BigInt(offset)
quickEdit(target)
}
</script>

<template>
Expand Down Expand Up @@ -94,14 +103,14 @@ function quickEdit(v: unknown, remove: boolean = false) {
<VueIcon :icon="rawValue ? 'i-material-symbols-check-box-sharp' : 'i-material-symbols-check-box-outline-blank-sharp'" />
</template>
</VueButton>
<!-- increment/decrement button, numeric value only -->
<template v-else-if="dataType === 'number'">
<VueButton v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEdit((rawValue as number) + 1)">
<!-- increment/decrement button, numeric/bigint -->
<template v-else-if="dataType === 'number' || customType === 'bigint'">
<VueButton v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEditNum(rawValue as number | string, 1)">
<template #icon>
<VueIcon icon="i-carbon-add" />
</template>
</VueButton>
<VueButton v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEdit((rawValue as number) - 1)">
<VueButton v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEditNum(rawValue as number | string, -1)">
<template #icon>
<VueIcon icon="i-carbon-subtract" />
</template>
Expand Down Expand Up @@ -129,7 +138,7 @@ function quickEdit(v: unknown, remove: boolean = false) {
<template #popper>
<div class="w160px py5px">
<VueDropdownButton
@click="copy(dataType === 'object' ? JSON.stringify(rawValue) : rawValue.toString())"
@click="copy(typeof rawValue === 'object' ? JSON.stringify(rawValue) : rawValue.toString())"
>
<template #icon>
<VueIcon icon="i-material-symbols-copy-all-rounded" class="mt4px" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { VueButton, VueIcon, VueInput, VTooltip as vTooltip } from '@vue/devtools-ui'
import { debounce } from 'perfect-debounce'
import { toSubmit } from '@vue/devtools-kit'
import { customTypeEnums, toSubmit } from '@vue/devtools-kit'

const props = withDefaults(defineProps<{
modelValue: string
type: string // typeof value
customType?: customTypeEnums
showActions?: boolean
autoFocus?: boolean
}>(), {
Expand All @@ -15,7 +15,7 @@ const props = withDefaults(defineProps<{

const emit = defineEmits<{
'cancel': []
'submit': [dataType: string]
'submit': []
'update:modelValue': [value: string]
}>()

Expand All @@ -28,14 +28,14 @@ watchEffect(() => {
if (escape.value)
emit('cancel')
else if (enter.value)
emit('submit', props.type)
emit('submit')
})

const value = useVModel(props, 'modelValue', emit)

function tryToParseJSONString(v: unknown) {
try {
toSubmit(v as string)
toSubmit(v as string, props.customType)
return true
}
catch {
Expand Down Expand Up @@ -67,7 +67,7 @@ watch(value, checkWarning())
<VueButton
v-tooltip="{
content: 'Enter to submit change',
}" size="mini" flat class="p2px!" @click.stop="$emit('submit', type)"
}" size="mini" flat class="p2px!" @click.stop="$emit('submit')"
>
<template #icon>
<VueIcon icon="i-material-symbols-save" />
Expand Down
28 changes: 14 additions & 14 deletions packages/client/src/components/inspector/InspectorStateField.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { InspectorCustomState, InspectorState, InspectorStateEditorPayload } from '@vue/devtools-kit'
import { isArray, isObject, sortByKey } from '@vue/devtools-shared'
import { formatInspectorStateValue, getInspectorStateValueType, getRawValue, toEdit, toSubmit } from '@vue/devtools-kit'
import { formatInspectorStateValue, getInspectorStateValueType, getRaw, toEdit, toSubmit } from '@vue/devtools-kit'
import { useDevToolsBridgeRpc } from '@vue/devtools-core'
import { VueButton, VueIcon, VTooltip as vTooltip } from '@vue/devtools-ui'
import Actions from './InspectorDataField/Actions.vue'
Expand Down Expand Up @@ -50,12 +50,12 @@ const normalizedValue = computed(() => {
}
})

const rawValue = computed(() => getRawValue(props.data.value))
const raw = computed(() => getRaw(props.data.value))

const limit = ref(STATE_FIELDS_LIMIT_SIZE)

const normalizedChildField = computed(() => {
const { value, inherit } = rawValue.value
const { value, inherit } = raw.value
let displayedValue: any[]
if (isArray(value)) {
const sliced = value.slice(0, limit.value)
Expand Down Expand Up @@ -86,7 +86,7 @@ const normalizedChildField = computed(() => {
})

const fieldsCount = computed(() => {
const { value } = rawValue.value
const { value } = raw.value
if (isArray(value))
return value.length
else if (isObject(value))
Expand All @@ -112,15 +112,15 @@ const { editingType, editing, editingText, toggleEditing, nodeId } = useStateEdi

watch(() => editing.value, (v) => {
if (v) {
const { value } = rawValue.value
editingText.value = toEdit(value)
const { value } = raw.value
editingText.value = toEdit(value, raw.value.customType)
}
else {
editingText.value = ''
}
})

function submit(dataType: string) {
function submit() {
const data = props.data
bridgeRpc.editInspectorState({
path: data.key.split('.'),
Expand All @@ -129,8 +129,8 @@ function submit(dataType: string) {
nodeId,
state: {
newKey: null!,
type: dataType,
value: toSubmit(editingText.value),
type: editingType.value,
value: toSubmit(editingText.value, raw.value.customType),
},
} satisfies InspectorStateEditorPayload)
toggleEditing()
Expand All @@ -142,7 +142,7 @@ const { addNewProp: addNewPropApi, draftingNewProp, resetDrafting } = useStateEd
function addNewProp(type: EditorAddNewPropType) {
if (!isExpanded.value)
toggleCollapse()
addNewPropApi(type, rawValue.value.value)
addNewPropApi(type, raw.value.value)
}

function submitDrafting() {
Expand Down Expand Up @@ -173,7 +173,7 @@ const { isHovering } = useHover(() => containerRef.value)
<div>
<span overflow-hidden text-ellipsis whitespace-nowrap state-key>{{ normalizedDisplayedKey }}</span>
<span mx-1>:</span>
<EditInput v-if="editing" v-model="editingText" :type="editingType" @cancel="toggleEditing" @submit="submit" />
<EditInput v-if="editing" v-model="editingText" :custom-type="raw.customType" @cancel="toggleEditing" @submit="submit" />
<template v-else>
<span :class="stateFormatClass">
<span v-html="normalizedValue" />
Expand All @@ -191,7 +191,7 @@ const { isHovering } = useHover(() => containerRef.value)
<ExpandIcon :value="isExpanded" absolute left--6 group-hover:text-white />
<span overflow-hidden text-ellipsis whitespace-nowrap state-key>{{ normalizedDisplayedKey }}</span>
<span mx-1>:</span>
<EditInput v-if="editing" v-model="editingText" :type="editingType" @cancel="toggleEditing" @submit="submit" />
<EditInput v-if="editing" v-model="editingText" :custom-type="raw.customType" @cancel="toggleEditing" @submit="submit" />
<template v-else>
<span :class="stateFormatClass">
<span v-html="normalizedValue" />
Expand All @@ -214,10 +214,10 @@ const { isHovering } = useHover(() => containerRef.value)
</VueButton>
<div v-if="draftingNewProp.enable" :style="{ paddingLeft: `${(depth + 1) * 15 + 4}px` }">
<span overflow-hidden text-ellipsis whitespace-nowrap state-key>
<EditInput v-model="draftingNewProp.key" type="string" :show-actions="false" />
<EditInput v-model="draftingNewProp.key" :show-actions="false" />
</span>
<span mx-1>:</span>
<EditInput v-model="draftingNewProp.value" type="string" :auto-focus="false" @cancel="resetDrafting" @submit="submitDrafting" />
<EditInput v-model="draftingNewProp.value" :auto-focus="false" @cancel="resetDrafting" @submit="submitDrafting" />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as format from '../format'
import { customTypeEnums } from '../../types'
import { INFINITY, NAN, NEGATIVE_INFINITY, UNDEFINED } from '../constants'

describe('format: displayText and rawValue can be calculated by formatInspectorStateValue, getRawValue', () => {
describe('format: displayText and rawValue can be calculated by formatInspectorStateValue, getRaw', () => {
describe('type: literals', () => {
// eslint-disable-next-line test/consistent-test-it
test.each([
Expand All @@ -16,7 +17,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS
{ literal: UNDEFINED, displayText: 'undefined' },
])('type: %s', (value) => {
const displayText = format.formatInspectorStateValue(value.literal)
const rawValue = format.getRawValue(value.literal).value
const rawValue = format.getRaw(value.literal).value

expect(displayText).toBe(value.displayText)
expect(rawValue).toBe(value.literal)
Expand All @@ -26,7 +27,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS
it('type: plain object', () => {
const value = { foo: 'bar' }
const displayText = format.formatInspectorStateValue(value)
const rawValue = format.getRawValue(value).value
const rawValue = format.getRaw(value).value

expect(displayText).toBe('Object')
expect(rawValue).toEqual(value)
Expand All @@ -35,7 +36,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS
it('type: array', () => {
const value = ['foo', { bar: 'baz' }]
const displayText = format.formatInspectorStateValue(value)
const rawValue = format.getRawValue(value).value
const rawValue = format.getRaw(value).value

expect(displayText).toBe('Array[2]')
expect(rawValue).toEqual(value)
Expand All @@ -45,7 +46,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS
it('type: common custom', () => {
const value = { _custom: { displayText: 'custom-display', value: Symbol(123) } }
const displayText = format.formatInspectorStateValue(value)
const rawValue = format.getRawValue(value).value
const rawValue = format.getRaw(value).value

expect(displayText).toBe(value._custom.displayText)
expect(rawValue).toEqual(value._custom.value)
Expand All @@ -62,7 +63,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS
}

const displayText = format.formatInspectorStateValue(value)
const rawValue = format.getRawValue(value).value
const rawValue = format.getRaw(value).value

expect(displayText).toBe(value._custom.value._custom.displayText)
expect(rawValue).toEqual(value._custom.value._custom.value)
Expand All @@ -87,8 +88,9 @@ describe('format: toEdit', () => {
{ value: { foo: NAN }, target: '{"foo":NaN}' },
{ value: { foo: NEGATIVE_INFINITY }, target: '{"foo":-Infinity}' },
{ value: { foo: UNDEFINED }, target: '{"foo":undefined}' },
{ value: '123', customType: 'bigint' as customTypeEnums, target: '123' },
])('value: $value will be deserialized to target', (value) => {
const deserialized = format.toEdit(value.value)
const deserialized = format.toEdit(value.value, value.customType)
expect(deserialized).toBe(value.target)
})
})
Expand All @@ -113,8 +115,9 @@ describe('format: toSubmit', () => {
{ value: '{"foo":undefined}', target: {} },
// Regex test: The token in key field kept untouched.
{ value: '{"undefined": NaN }', target: { undefined: Number.NaN } },
{ value: '123', customType: 'bigint' as customTypeEnums, target: BigInt(123) },
])('value: $value will be serialized to target', (value) => {
const serialized = format.toSubmit(value.value)
const serialized = format.toSubmit(value.value, value.customType)
expect(serialized).toStrictEqual(value.target)
})
})
16 changes: 8 additions & 8 deletions packages/devtools-kit/src/core/component/state/custom.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { InspectorState } from '../types'
import type { InspectorState, customTypeEnums } from '../types'
import { getComponentName, getInstanceName } from '../general/util'
import { processInstanceState } from './process'
import { escape, getSetupStateType, toRaw } from './util'
Expand All @@ -21,7 +21,7 @@ export function getFunctionDetails(func: Function) {
const name = typeof func.name === 'string' ? func.name : ''
return {
_custom: {
type: 'function',
type: 'function' satisfies customTypeEnums,
displayText: `<span style="opacity:.5;">function</span> ${escape(name)}${args}`,
tooltipText: string.trim() ? `<pre>${string}</pre>` : null,
},
Expand Down Expand Up @@ -64,7 +64,7 @@ export function getSetDetails(val: Set<unknown>) {
const list = Array.from(val)
return {
_custom: {
type: 'set',
type: 'set' satisfies customTypeEnums,
displayText: `Set[${list.length}]`,
value: list,
readOnly: true,
Expand Down Expand Up @@ -120,7 +120,7 @@ function namedNodeMapToObject(map: NamedNodeMap) {
export function getStoreDetails(store) {
return {
_custom: {
type: 'store',
type: 'store' satisfies customTypeEnums,
displayText: 'Store',
value: {
state: store.state,
Expand Down Expand Up @@ -179,7 +179,7 @@ export function getComponentDefinitionDetails(definition) {
}
return {
_custom: {
type: 'component-definition',
type: 'component-definition' satisfies customTypeEnums,
displayText: display,
tooltipText: 'Component definition',
...definition.__file
Expand All @@ -195,7 +195,7 @@ export function getHTMLElementDetails(value: HTMLElement) {
try {
return {
_custom: {
type: 'HTMLElement',
type: 'HTMLElement' satisfies customTypeEnums,
displayText: `<span class="opacity-30">&lt;</span><span class="text-blue-500">${value.tagName.toLowerCase()}</span><span class="opacity-30">&gt;</span>`,
value: namedNodeMapToObject(value.attributes),
},
Expand All @@ -204,7 +204,7 @@ export function getHTMLElementDetails(value: HTMLElement) {
catch (e) {
return {
_custom: {
type: 'HTMLElement',
type: 'HTMLElement' satisfies customTypeEnums,
displayText: `<span class="text-blue-500">${String(value)}</span>`,
},
}
Expand Down Expand Up @@ -232,7 +232,7 @@ export function getObjectDetails(object: Record<string, any>) {
if (typeof object.__asyncLoader === 'function') {
return {
_custom: {
type: 'component-definition',
type: 'component-definition' satisfies customTypeEnums,
display: 'Async component definition',
},
}
Expand Down
Loading