Skip to content

Commit 3addaad

Browse files
feat(kit): optimize editing on native set (#269)
1 parent a5a1aba commit 3addaad

File tree

3 files changed

+172
-8
lines changed

3 files changed

+172
-8
lines changed

packages/client/src/components/inspector/InspectorStateField.vue

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,21 @@ const raw = computed(() => getRaw(props.data.value))
5353
5454
const limit = ref(STATE_FIELDS_LIMIT_SIZE)
5555
56-
const normalizedChildField = computed(() => {
57-
const { value, inherit } = raw.value
56+
const normalizedChildField = computed<
57+
Record<string, InspectorState>
58+
>(() => {
59+
const { value, inherit, customType } = raw.value
60+
// The member in native set can only be added or removed.
61+
// It cannot be modified.
62+
const isUneditableType = customType === 'set'
5863
let displayedValue: any[]
5964
if (isArray(value)) {
6065
const sliced = value.slice(0, limit.value)
6166
return sliced.map((item, i) => ({
6267
key: `${props.data.key}.${i}`,
6368
value: item,
6469
...inherit,
65-
editable: props.data.editable,
70+
editable: props.data.editable && !isUneditableType,
6671
creating: false,
6772
}))
6873
}
@@ -71,7 +76,7 @@ const normalizedChildField = computed(() => {
7176
key: `${props.data.key}.${key}`,
7277
value: value[key],
7378
...inherit,
74-
editable: props.data.editable,
79+
editable: props.data.editable && !isUneditableType,
7580
creating: false,
7681
}))
7782
if (type.value !== 'custom')

packages/devtools-kit/__tests__/component/editor.test.ts

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { RefStateEditor } from '../../src/core/component/state/editor'
1+
import { RefStateEditor, StateEditor } from '../../src/core/component/state/editor'
22

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

6+
// Plain object
67
// eslint-disable-next-line test/consistent-test-it
78
test.each([
9+
// Plain object.
810
// Add new key.
911
{ refValue: { foo: 'bar' }, newValue: { foo: 'bar', bar: 'baz' } },
1012
// Add new key and modify origin value.
@@ -21,4 +23,158 @@ describe('editor: RefStateEditor', () => {
2123
editor.set(refValue as any, newValue)
2224
expect(refValue).toEqual(newValue)
2325
})
26+
27+
// Native set
28+
it('refStateEditor.set on native set', () => {
29+
const refValue = new Set(['foo', 'bar'])
30+
const newValue = ['baz', 'foo', 'bar']
31+
const targetValue = new Set(['baz', 'foo', 'bar'])
32+
editor.set(refValue as any, newValue)
33+
expect(refValue).toEqual(targetValue)
34+
})
35+
36+
// Native map
37+
// eslint-disable-next-line test/consistent-test-it
38+
test.each([
39+
// Add new key.
40+
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'bar', bar: 'baz' }, targetValue: new Map([['foo', 'bar'], ['bar', 'baz']]) },
41+
// Add new key and modify origin value.
42+
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'barr', bar: 'baz' }, targetValue: new Map([['foo', 'barr'], ['bar', 'baz']]) },
43+
// Add new key and modify origin value.
44+
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'barr', bar: 'baz' }, targetValue: new Map([['foo', 'barr'], ['bar', 'baz']]) },
45+
// Modify origin value.
46+
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'barr' }, targetValue: new Map([['foo', 'barr']]) },
47+
// Remove key.
48+
{ refValue: new Map([['foo', 'bar']]), newValue: {}, targetValue: new Map() },
49+
// Remove key and modify origin value.
50+
{ refValue: new Map([['foo', 'bar']]), newValue: { foo: 'barr' }, targetValue: new Map([['foo', 'barr']]) },
51+
// Remove key and add new key.
52+
{ refValue: new Map([['foo', 'bar']]), newValue: { bar: 'baz' }, targetValue: new Map([['bar', 'baz']]) },
53+
])('%o can be modified to $newValue by RefStateEditor.set', ({ refValue, newValue, targetValue }) => {
54+
editor.set(refValue as any, newValue)
55+
expect(refValue).toEqual(targetValue || newValue)
56+
})
57+
})
58+
59+
describe('editor: StateEditor.set', () => {
60+
const stateEditor = new StateEditor()
61+
62+
describe('editComponentState: plain object', () => {
63+
it('modify value', () => {
64+
const target = { foo: 'bar' }
65+
const newValue = 'baz'
66+
const state = { newKey: '', type: '', value: 'baz' }
67+
const path = 'foo'
68+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
69+
stateEditor.set(target, path, newValue, defaultCallback)
70+
expect(target).toEqual({ foo: 'baz' })
71+
})
72+
73+
it('add new value', () => {
74+
const target = { foo: 'bar' }
75+
const newValue = 'baz'
76+
const state = { newKey: 'bar', type: '', value: 'baz' }
77+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
78+
stateEditor.set(target, '', newValue, defaultCallback)
79+
expect(target).toEqual({ foo: 'bar', bar: 'baz' })
80+
})
81+
82+
it('remove value', () => {
83+
const target = { foo: 'bar', bar: 'baz' }
84+
const state = { newKey: '', type: '', value: '', remove: true }
85+
const path = 'foo'
86+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
87+
stateEditor.set(target, path, '', defaultCallback)
88+
expect(target).toEqual({ bar: 'baz' })
89+
})
90+
})
91+
92+
describe('editComponentState: array', () => {
93+
it('modify value', () => {
94+
const target = ['foo', 'bar']
95+
const state = { newKey: '', type: '', value: 'baz' }
96+
const newValue = 'baz'
97+
const path = '0'
98+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
99+
stateEditor.set(target, path, newValue, defaultCallback)
100+
expect(target).toEqual(['baz', 'bar'])
101+
})
102+
103+
it('add new value', () => {
104+
const target = ['foo', 'bar']
105+
const newValue = 'baz'
106+
const state = { newKey: '2', type: '', value: newValue }
107+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
108+
stateEditor.set(target, '2', newValue, defaultCallback)
109+
expect(target).toEqual(['foo', 'bar', 'baz'])
110+
})
111+
112+
it('remove value', () => {
113+
const target = ['foo', 'bar', 'baz']
114+
const state = { newKey: '', type: '', value: '', remove: true }
115+
const path = '0'
116+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
117+
stateEditor.set(target, path, '', defaultCallback)
118+
expect(target).toEqual(['bar', 'baz'])
119+
})
120+
})
121+
122+
describe('editComponentState: set', () => {
123+
it('add new value', () => {
124+
const target = new Set(['foo', 'bar'])
125+
const newValue = 'baz'
126+
const state = { newKey: '2', type: '', value: newValue }
127+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
128+
stateEditor.set(target, '2', newValue, defaultCallback)
129+
expect(target).toEqual(new Set(['foo', 'bar', 'baz']))
130+
})
131+
132+
it('remove value', () => {
133+
const target = new Set(['foo', 'bar', 'baz'])
134+
const state = { newKey: '', type: '', value: 'foo', remove: true }
135+
const path = '0'
136+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
137+
stateEditor.set(target, path, '', defaultCallback)
138+
expect(target).toEqual(new Set(['bar', 'baz']))
139+
})
140+
141+
it('remove object type member', () => {
142+
const target = new Set(['foo', { bar: 'baz' }])
143+
const state = { newKey: '', type: '', value: { bar: 'baz' }, remove: true }
144+
const path = '1'
145+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
146+
stateEditor.set(target, path, '', defaultCallback)
147+
expect(target).toEqual(new Set(['foo']))
148+
})
149+
})
150+
151+
describe('editComponentState: map', () => {
152+
it('modify value', () => {
153+
const target = new Map([['foo', 'bar']])
154+
const state = { newKey: '', type: '', value: 'baz' }
155+
const newValue = 'baz'
156+
const path = 'foo'
157+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
158+
stateEditor.set(target, path, newValue, defaultCallback)
159+
expect(target).toEqual(new Map([['foo', 'baz']]))
160+
})
161+
162+
it('add new value', () => {
163+
const target = new Map([['foo', 'bar']])
164+
const newValue = 'baz'
165+
const state = { newKey: 'bar', type: '', value: newValue }
166+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
167+
stateEditor.set(target, 'bar', newValue, defaultCallback)
168+
expect(target).toEqual(new Map([['foo', 'bar'], ['bar', 'baz']]))
169+
})
170+
171+
it('remove value', () => {
172+
const target = new Map([['foo', 'bar'], ['bar', 'baz']])
173+
const state = { newKey: '', type: '', value: '', remove: true }
174+
const path = 'foo'
175+
const defaultCallback = stateEditor.createDefaultSetCallback(state)
176+
stateEditor.set(target, path, '', defaultCallback)
177+
expect(target).toEqual(new Map([['bar', 'baz']]))
178+
})
179+
})
24180
})

packages/devtools-kit/src/core/component/state/editor.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ export class StateEditor {
2222
const section = sections.shift()!
2323
if (object instanceof Map)
2424
object = object.get(section) as Recordable
25-
else
26-
object = object[section] as Recordable
25+
if (object instanceof Set)
26+
object = Array.from(object.values())[section] as Recordable
27+
else object = object[section] as Recordable
2728
if (this.refEditor.isRef(object))
2829
object = this.refEditor.get(object)
2930
}
@@ -79,7 +80,7 @@ export class StateEditor {
7980
else if (toRaw(object) instanceof Map)
8081
object.delete(field)
8182
else if (toRaw(object) instanceof Set)
82-
object.delete(value)
83+
object.delete(Array.from(object.values())[field as number])
8384
else Reflect.deleteProperty(object, field)
8485
}
8586
if (!state.remove) {
@@ -88,6 +89,8 @@ export class StateEditor {
8889
this.refEditor.set(target, value)
8990
else if (toRaw(object) instanceof Map)
9091
object.set(state.newKey || field, value)
92+
else if (toRaw(object) instanceof Set)
93+
object.add(value)
9194
else
9295
object[state.newKey || field] = value
9396
}

0 commit comments

Comments
 (0)