Skip to content

Commit 0d5374b

Browse files
authored
132 Option to insert properties/values at the beginning (#136)
* Implement "insertAtTop" option
1 parent 956bef2 commit 0d5374b

File tree

7 files changed

+39
-21
lines changed

7 files changed

+39
-21
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ The only *required* value is `data` (although you will need to provide a `setDat
150150
| `jsonStringify` | `(data: JsonData) => string` | `(data) => JSON.stringify(data, null, 2)` | Similarly, you can override the default presentation of the JSON string when starting editing JSON. You can supply different formatting parameters to the native `JSON.stringify()`, or provide a third-party option, like the aforementioned JSON5. |
151151
| `errorMessageTimeout` | `number` | `2500` | Time (in milliseconds) to display the error message in the UI. | |
152152
| `keyboardControls` | `KeyboardControls` | As explained [above](#usage) | Override some or all of the keyboard controls. See [Keyboard customisation](#keyboard-customisation) for details. | |
153+
| `insertAtTop` | `boolean\| "object \| "array"` | `false` | If `true`, inserts new values at the *top* rather than bottom. Can set the behaviour just for arrays or objects by setting to `"object"` or `"array"` respectively. | |
153154

154155
## Managing state
155156

@@ -750,7 +751,9 @@ This component is heavily inspired by [react-json-view](https://github.com/mac-s
750751
751752
## Changelog
752753
753-
- **1.18.0**: Ability to [customise keyboard controls](#keyboard-customisation)
754+
- **1.18.0**:
755+
- Ability to [customise keyboard controls](#keyboard-customisation)
756+
- Option to insert new values at the top
754757
- **1.17.0**: `defaultValue` function takes the new `key` as second parameter
755758
- **1.16.0**: Extend the "click" zone for collapsing nodes to the header bar and left margin (not just the collapse icon)
756759
- **1.15.12**:

demo/src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ function App() {
395395
// collapseModifier: 'Control',
396396
// booleanConfirm: 'Enter',
397397
// }}
398+
// insertAtBeginning="object"
398399
/>
399400
</Box>
400401
<VStack w="100%" align="flex-end" gap={4}>

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@
4040
"wrap-ansi": "7.0.0"
4141
},
4242
"dependencies": {
43-
"object-property-assigner": "^1.3.1",
44-
"object-property-extractor": "^1.0.12"
43+
"object-property-assigner": "^1.3.5",
44+
"object-property-extractor": "^1.0.13"
4545
},
4646
"devDependencies": {
4747
"@rollup/plugin-terser": "^0.4.4",

src/CollectionNode.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export const CollectionNode: React.FC<CollectionNodeProps> = (props) => {
4646
jsonStringify,
4747
keyboardControls,
4848
handleKeyboard,
49+
insertAtTop,
4950
} = props
5051
const [stringifiedValue, setStringifiedValue] = useState(jsonStringify(data))
5152

@@ -171,13 +172,16 @@ export const CollectionNode: React.FC<CollectionNodeProps> = (props) => {
171172
animateCollapse(false)
172173
const newValue = getDefaultNewValue(nodeData, key)
173174
if (collectionType === 'array') {
174-
onAdd(newValue, [...path, (data as unknown[]).length]).then((error) => {
175+
const index = insertAtTop.array ? 0 : (data as unknown[]).length
176+
const options = insertAtTop.array ? { insert: true } : {}
177+
onAdd(newValue, [...path, index], options).then((error) => {
175178
if (error) onError({ code: 'ADD_ERROR', message: error }, newValue as CollectionData)
176179
})
177180
} else if (key in data) {
178181
onError({ code: 'KEY_EXISTS', message: translate('ERROR_KEY_EXISTS', nodeData) }, key)
179182
} else {
180-
onAdd(newValue, [...path, key]).then((error) => {
183+
const options = insertAtTop.object ? { insertBefore: 0 } : {}
184+
onAdd(newValue, [...path, key], options).then((error) => {
181185
if (error) onError({ code: 'ADD_ERROR', message: error }, newValue as CollectionData)
182186
})
183187
}

src/JsonEditor.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useCallback, useEffect, useMemo, useState } from 'react'
2-
import assign, { type Input } from 'object-property-assigner'
2+
import assign, { type Options as AssignOptions, type Input } from 'object-property-assigner'
33
import extract from 'object-property-extractor'
44
import { CollectionNode } from './CollectionNode'
55
import {
@@ -72,6 +72,7 @@ const Editor: React.FC<JsonEditorProps> = ({
7272
jsonStringify = (data: JsonData) => JSON.stringify(data, null, 2),
7373
errorMessageTimeout = 2500,
7474
keyboardControls = {},
75+
insertAtTop = false,
7576
}) => {
7677
const { getStyles } = useTheme()
7778
const collapseFilter = useCallback(getFilterFunction(collapse), [collapse])
@@ -166,12 +167,13 @@ const Editor: React.FC<JsonEditorProps> = ({
166167
})
167168
}
168169

169-
const onAdd: InternalUpdateFunction = async (value, path) => {
170+
const onAdd: InternalUpdateFunction = async (value, path, options) => {
170171
const { currentData, newData, currentValue, newValue } = updateDataObject(
171172
data,
172173
path,
173174
value,
174-
'add'
175+
'add',
176+
options
175177
)
176178

177179
return await handleEdit(srcAdd, {
@@ -237,7 +239,7 @@ const Editor: React.FC<JsonEditorProps> = ({
237239
[...targetPath, targetKey],
238240
currentValue,
239241
'add',
240-
insertOptions as AssignOptions
242+
insertOptions as UpdateOptions
241243
)
242244

243245
return await handleEdit(srcEdit, {
@@ -304,6 +306,10 @@ const Editor: React.FC<JsonEditorProps> = ({
304306
errorMessageTimeout,
305307
handleKeyboard: handleKeyboardCallback,
306308
keyboardControls: fullKeyboardControls,
309+
insertAtTop: {
310+
object: insertAtTop === true || insertAtTop === 'object',
311+
array: insertAtTop === true || insertAtTop === 'array',
312+
},
307313
}
308314

309315
const mainContainerStyles = { ...getStyles('container', nodeData), minWidth, maxWidth }
@@ -335,19 +341,19 @@ export const JsonEditor: React.FC<JsonEditorProps> = (props) => {
335341
)
336342
}
337343

338-
interface AssignOptions {
344+
interface UpdateOptions {
339345
remove?: boolean
340-
insert?: true
341-
insertBefore?: string
342-
insertAfter?: string
346+
insert?: boolean
347+
insertBefore?: string | number
348+
insertAfter?: string | number
343349
}
344350

345351
const updateDataObject = (
346352
data: JsonData,
347353
path: Array<string | number>,
348354
newValue: unknown,
349355
action: 'update' | 'delete' | 'add',
350-
insertOptions: { insert?: true; insertBefore?: string; insertAfter?: string } = {}
356+
insertOptions: AssignOptions = {}
351357
) => {
352358
if (path.length === 0) {
353359
return {
@@ -358,7 +364,7 @@ const updateDataObject = (
358364
}
359365
}
360366

361-
const assignOptions: AssignOptions = {
367+
const assignOptions: UpdateOptions = {
362368
remove: action === 'delete',
363369
...insertOptions,
364370
}

src/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { type Options as AssignOptions } from 'object-property-assigner'
12
import { type LocalisedStrings, type TranslateFunction } from './localisation'
23

34
export type JsonData = CollectionData | ValueData
@@ -46,6 +47,7 @@ export interface JsonEditorProps {
4647
jsonStringify?: (input: JsonData) => string
4748
errorMessageTimeout?: number // ms
4849
keyboardControls?: KeyboardControls
50+
insertAtTop?: boolean | 'array' | 'object'
4951
}
5052

5153
const ValueDataTypes = ['string', 'number', 'boolean', 'null'] as const
@@ -140,7 +142,8 @@ export type CompareFunction = (a: string, b: string) => number
140142
// Internal update
141143
export type InternalUpdateFunction = (
142144
value: unknown,
143-
path: CollectionKey[]
145+
path: CollectionKey[],
146+
options?: AssignOptions
144147
) => Promise<string | void>
145148

146149
// For drag-n-drop
@@ -236,6 +239,7 @@ export interface CollectionNodeProps extends BaseNodeProps {
236239
defaultValue: unknown
237240
jsonParse: (input: string) => JsonData
238241
jsonStringify: (data: JsonData) => string
242+
insertAtTop: { object: boolean; array: boolean }
239243
}
240244

241245
export type ValueData = string | number | boolean

yarn.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2433,12 +2433,12 @@ object-keys@^1.1.1:
24332433
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
24342434
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
24352435

2436-
object-property-assigner@^1.3.1:
2437-
version "1.3.2"
2438-
resolved "https://registry.yarnpkg.com/object-property-assigner/-/object-property-assigner-1.3.2.tgz#9630ab7cb39e8507c0a836771863b7eb3790b216"
2439-
integrity sha512-zhRYPERkZMJ2SATAFVIJLBTfWMd8r2dR4sgpAiObgEfL7YnsAhPPOQChH97pf+rfIwOWgtJsz1TDadOQQgjQzA==
2436+
object-property-assigner@^1.3.5:
2437+
version "1.3.5"
2438+
resolved "https://registry.yarnpkg.com/object-property-assigner/-/object-property-assigner-1.3.5.tgz#f5037ff73b7b015a42e4059c3ff5c84017413ca2"
2439+
integrity sha512-DIzHzNSTnpoG8QPQCDNrHa6O3vLMhktK3Igirqpk523UYIVe8JNCKcn5C9WyLQxJc58EGsAIiiEu10gqPrud8w==
24402440

2441-
object-property-extractor@^1.0.12:
2441+
object-property-extractor@^1.0.13:
24422442
version "1.0.13"
24432443
resolved "https://registry.yarnpkg.com/object-property-extractor/-/object-property-extractor-1.0.13.tgz#fff188d6308ac5574eb2610221af906eb87b4f38"
24442444
integrity sha512-9kgEjTWDhTPuPn7nyof+5mLmCKBPKdU0c7IVpTbOvYKYSdXQ5skH4Pa/8MPbZXeyXBGrqS82JyWecsh6tMxiLw==

0 commit comments

Comments
 (0)