Skip to content
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

feat(kit): optimize editing on native set #269

Merged
merged 4 commits into from
Mar 11, 2024
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
13 changes: 9 additions & 4 deletions packages/client/src/components/inspector/InspectorStateField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,21 @@ const raw = computed(() => getRaw(props.data.value))

const limit = ref(STATE_FIELDS_LIMIT_SIZE)

const normalizedChildField = computed(() => {
const { value, inherit } = raw.value
const normalizedChildField = computed<
Record<string, InspectorState>
>(() => {
const { value, inherit, customType } = raw.value
// The member in native set can only be added or removed.
// It cannot be modified.
const isUneditableType = customType === 'set'
let displayedValue: any[]
if (isArray(value)) {
const sliced = value.slice(0, limit.value)
return sliced.map((item, i) => ({
key: `${props.data.key}.${i}`,
value: item,
...inherit,
editable: props.data.editable,
editable: props.data.editable && !isUneditableType,
creating: false,
}))
}
Expand All @@ -71,7 +76,7 @@ const normalizedChildField = computed(() => {
key: `${props.data.key}.${key}`,
value: value[key],
...inherit,
editable: props.data.editable,
editable: props.data.editable && !isUneditableType,
creating: false,
}))
if (type.value !== 'custom')
Expand Down
158 changes: 157 additions & 1 deletion packages/devtools-kit/__tests__/component/editor.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { RefStateEditor } from '../../src/core/component/state/editor'
import { RefStateEditor, StateEditor } from '../../src/core/component/state/editor'

describe('editor: RefStateEditor', () => {
const editor = new RefStateEditor()

// Plain object
// eslint-disable-next-line test/consistent-test-it
test.each([
// Plain object.
// Add new key.
{ refValue: { foo: 'bar' }, newValue: { foo: 'bar', bar: 'baz' } },
// Add new key and modify origin value.
Expand All @@ -21,4 +23,158 @@ describe('editor: RefStateEditor', () => {
editor.set(refValue as any, newValue)
expect(refValue).toEqual(newValue)
})

// Native set
it('refStateEditor.set on native set', () => {
const refValue = new Set(['foo', 'bar'])
const newValue = ['baz', 'foo', 'bar']
const targetValue = new Set(['baz', 'foo', 'bar'])
editor.set(refValue as any, newValue)
expect(refValue).toEqual(targetValue)
})

// Native map
// eslint-disable-next-line test/consistent-test-it
test.each([
// Add new key.
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'bar', bar: 'baz' }, targetValue: new Map([['foo', 'bar'], ['bar', 'baz']]) },
// Add new key and modify origin value.
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'barr', bar: 'baz' }, targetValue: new Map([['foo', 'barr'], ['bar', 'baz']]) },
// Add new key and modify origin value.
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'barr', bar: 'baz' }, targetValue: new Map([['foo', 'barr'], ['bar', 'baz']]) },
// Modify origin value.
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'barr' }, targetValue: new Map([['foo', 'barr']]) },
// Remove key.
{ refValue: new Map([['foo', 'bar']]), newValue: {}, targetValue: new Map() },
// Remove key and modify origin value.
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'barr' }, targetValue: new Map([['foo', 'barr']]) },
// Remove key and add new key.
{ refValue: new Map([['foo', 'bar']]), newValue: { bar: 'baz' }, targetValue: new Map([['bar', 'baz']]) },
])('%o can be modified to $newValue by RefStateEditor.set', ({ refValue, newValue, targetValue }) => {
editor.set(refValue as any, newValue)
expect(refValue).toEqual(targetValue || newValue)
})
})

describe('editor: StateEditor.set', () => {
const stateEditor = new StateEditor()

describe('editComponentState: plain object', () => {
it('modify value', () => {
const target = { foo: 'bar' }
const newValue = 'baz'
const state = { newKey: '', type: '', value: 'baz' }
const path = 'foo'
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, path, newValue, defaultCallback)
expect(target).toEqual({ foo: 'baz' })
})

it('add new value', () => {
const target = { foo: 'bar' }
const newValue = 'baz'
const state = { newKey: 'bar', type: '', value: 'baz' }
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, '', newValue, defaultCallback)
expect(target).toEqual({ foo: 'bar', bar: 'baz' })
})

it('remove value', () => {
const target = { foo: 'bar', bar: 'baz' }
const state = { newKey: '', type: '', value: '', remove: true }
const path = 'foo'
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, path, '', defaultCallback)
expect(target).toEqual({ bar: 'baz' })
})
})

describe('editComponentState: array', () => {
it('modify value', () => {
const target = ['foo', 'bar']
const state = { newKey: '', type: '', value: 'baz' }
const newValue = 'baz'
const path = '0'
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, path, newValue, defaultCallback)
expect(target).toEqual(['baz', 'bar'])
})

it('add new value', () => {
const target = ['foo', 'bar']
const newValue = 'baz'
const state = { newKey: '2', type: '', value: newValue }
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, '2', newValue, defaultCallback)
expect(target).toEqual(['foo', 'bar', 'baz'])
})

it('remove value', () => {
const target = ['foo', 'bar', 'baz']
const state = { newKey: '', type: '', value: '', remove: true }
const path = '0'
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, path, '', defaultCallback)
expect(target).toEqual(['bar', 'baz'])
})
})

describe('editComponentState: set', () => {
it('add new value', () => {
const target = new Set(['foo', 'bar'])
const newValue = 'baz'
const state = { newKey: '2', type: '', value: newValue }
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, '2', newValue, defaultCallback)
expect(target).toEqual(new Set(['foo', 'bar', 'baz']))
})

it('remove value', () => {
const target = new Set(['foo', 'bar', 'baz'])
const state = { newKey: '', type: '', value: 'foo', remove: true }
const path = '0'
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, path, '', defaultCallback)
expect(target).toEqual(new Set(['bar', 'baz']))
})

it('remove object type member', () => {
const target = new Set(['foo', { bar: 'baz' }])
const state = { newKey: '', type: '', value: { bar: 'baz' }, remove: true }
const path = '1'
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, path, '', defaultCallback)
expect(target).toEqual(new Set(['foo']))
})
})

describe('editComponentState: map', () => {
it('modify value', () => {
const target = new Map([['foo', 'bar']])
const state = { newKey: '', type: '', value: 'baz' }
const newValue = 'baz'
const path = 'foo'
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, path, newValue, defaultCallback)
expect(target).toEqual(new Map([['foo', 'baz']]))
})

it('add new value', () => {
const target = new Map([['foo', 'bar']])
const newValue = 'baz'
const state = { newKey: 'bar', type: '', value: newValue }
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, 'bar', newValue, defaultCallback)
expect(target).toEqual(new Map([['foo', 'bar'], ['bar', 'baz']]))
})

it('remove value', () => {
const target = new Map([['foo', 'bar'], ['bar', 'baz']])
const state = { newKey: '', type: '', value: '', remove: true }
const path = 'foo'
const defaultCallback = stateEditor.createDefaultSetCallback(state)
stateEditor.set(target, path, '', defaultCallback)
expect(target).toEqual(new Map([['bar', 'baz']]))
})
})
})
9 changes: 6 additions & 3 deletions packages/devtools-kit/src/core/component/state/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ export class StateEditor {
const section = sections.shift()!
if (object instanceof Map)
object = object.get(section) as Recordable
else
object = object[section] as Recordable
if (object instanceof Set)
object = Array.from(object.values())[section] as Recordable
else object = object[section] as Recordable
if (this.refEditor.isRef(object))
object = this.refEditor.get(object)
}
Expand Down Expand Up @@ -79,7 +80,7 @@ export class StateEditor {
else if (toRaw(object) instanceof Map)
object.delete(field)
else if (toRaw(object) instanceof Set)
object.delete(value)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

object.delete(value) can not delete object-typed value in set.

object.delete(Array.from(object.values())[field as number])
else Reflect.deleteProperty(object, field)
}
if (!state.remove) {
Expand All @@ -88,6 +89,8 @@ export class StateEditor {
this.refEditor.set(target, value)
else if (toRaw(object) instanceof Map)
object.set(state.newKey || field, value)
else if (toRaw(object) instanceof Set)
object.add(value)
else
object[state.newKey || field] = value
}
Expand Down
Loading