Skip to content

Commit e2edc17

Browse files
authored
#12 #15 #16 #17 #18 #19 Custom nodes and more (#20)
- [Custom nodes](#custom-nodes) - Allow editing of keys - Option to define restrictions on data type selection - Option to hide array/object item counts - Improve keyboard interaction
1 parent 8c7d03c commit e2edc17

17 files changed

+1673
-1014
lines changed

README.md

Lines changed: 88 additions & 26 deletions
Large diffs are not rendered by default.

demo/package.json

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,33 @@
44
"private": true,
55
"homepage": "https://carlosnz.github.io/json-edit-react",
66
"dependencies": {
7-
"@chakra-ui/icons": "^2.0.19",
8-
"@chakra-ui/react": "^2.6.1",
9-
"@emotion/react": "^11.11.0",
7+
"@chakra-ui/icons": "^2.1.1",
8+
"@chakra-ui/react": "^2.8.2",
9+
"@emotion/react": "^11.11.1",
1010
"@emotion/styled": "^11.11.0",
11-
"@testing-library/jest-dom": "^5.16.5",
12-
"@testing-library/react": "^13.4.0",
13-
"@testing-library/user-event": "^13.5.0",
14-
"@types/jest": "^27.5.2",
15-
"@types/node": "^16.18.27",
16-
"@types/react": "^18.2.6",
17-
"@types/react-dom": "^18.2.4",
11+
"@testing-library/jest-dom": "^6.1.4",
12+
"@testing-library/react": "^14.1.0",
13+
"@testing-library/user-event": "^14.5.1",
14+
"@types/jest": "^29.5.8",
15+
"@types/node": "^20.9.0",
16+
"@types/react": "^18.2.37",
17+
"@types/react-dom": "^18.2.15",
1818
"firebase": "^9.22.1",
19-
"framer-motion": "^10.12.12",
20-
"json-edit-react": "^0.9.4",
19+
"framer-motion": "^10.16.5",
20+
"json-edit-react": "^0.9.6",
2121
"just-clone": "^6.2.0",
2222
"just-compare": "^2.3.0",
2323
"react": "^18.2.0",
2424
"react-dom": "^18.2.0",
2525
"react-firebase-hooks": "^5.1.1",
26-
"react-icons": "^4.8.0",
26+
"react-icons": "^4.12.0",
2727
"react-scripts": "5.0.1",
28-
"typescript": "^4.9.5",
28+
"typescript": "^5.2.2",
2929
"use-undo": "^1.1.1",
30-
"web-vitals": "^2.1.4"
30+
"web-vitals": "^3.5.0"
3131
},
3232
"scripts": {
33-
"start": "rimraf ./src/json-edit-react && mkdir ./src/json-edit-react && mkdir ./src/json-edit-react/src && concurrently --kill-others-on-fail \"PORT=3005 react-scripts start\" \"nodemon watch.js\"",
33+
"start": "rimraf ./src/json-edit-react && mkdir ./src/json-edit-react && mkdir ./src/json-edit-react/src && concurrently --kill-others-on-fail \"PORT=3008 react-scripts start\" \"nodemon watch.js\"",
3434
"build": "react-scripts build",
3535
"prebuild": "node ./scripts/getVersion.js",
3636
"test": "react-scripts test",
@@ -64,9 +64,9 @@
6464
]
6565
},
6666
"devDependencies": {
67-
"concurrently": "^8.0.1",
68-
"gh-pages": "^5.0.0",
69-
"node-fetch": "^3.3.1",
70-
"nodemon": "^2.0.22"
67+
"concurrently": "^8.2.2",
68+
"gh-pages": "^6.0.0",
69+
"node-fetch": "^3.3.2",
70+
"nodemon": "^3.0.1"
7171
}
7272
}

demo/src/App.tsx

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React, { useEffect, useRef } from 'react'
22
/* Local version */
3-
import JsonEditor, { themes, ThemeName, Theme, ThemeInput } from './json-edit-react/src'
3+
import { JsonEditor, themes, ThemeName, Theme, ThemeInput } from './json-edit-react/src'
44
/* npm version */
5-
// import JsonEditor, { themes, ThemeName, Theme, ThemeInput } from 'json-edit-react'
5+
// import { JsonEditor, themes, ThemeName, Theme, ThemeInput } from 'json-edit-react'
66
import { FaNpm, FaExternalLinkAlt, FaGithub } from 'react-icons/fa'
77
import { BiReset } from 'react-icons/bi'
88
import { AiOutlineCloudUpload } from 'react-icons/ai'
@@ -42,9 +42,10 @@ import { version } from './version'
4242

4343
function App() {
4444
const [selectedData, setSelectedData] = useState('basic')
45-
const [rootName, setRootName] = useState('data')
45+
const [rootName, setRootName] = useState(demoData[selectedData].rootName ?? 'data')
4646
const [indent, setIndent] = useState(4)
4747
const [collapseLevel, setCollapseLevel] = useState(2)
48+
const [showCount, setShowCount] = useState<'Yes' | 'No' | 'When closed'>('Yes')
4849
const [theme, setTheme] = useState<ThemeInput>('default')
4950
const [allowEdit, setAllowEdit] = useState(true)
5051
const [allowDelete, setAllowDelete] = useState(true)
@@ -81,6 +82,8 @@ function App() {
8182

8283
useEffect(() => {
8384
if (selectedData === 'editTheme') setTheme(data)
85+
const rootName = demoData[selectedData].rootName
86+
setRootName(rootName ?? 'data')
8487
}, [data])
8588

8689
const restrictEdit: FilterFunction | boolean = (() => {
@@ -210,6 +213,9 @@ function App() {
210213
if (selectedData === 'editTheme') setTheme(newData)
211214
}}
212215
collapse={collapseLevel}
216+
showCollectionCount={
217+
showCount === 'Yes' ? true : showCount === 'When closed' ? 'when-closed' : false
218+
}
213219
enableClipboard={
214220
allowCopy
215221
? ({ stringValue, type }) =>
@@ -225,12 +231,14 @@ function App() {
225231
restrictEdit={restrictEdit}
226232
restrictDelete={restrictDelete}
227233
restrictAdd={restrictAdd}
234+
restrictTypeSelection={demoData[selectedData]?.restrictTypeSelection}
228235
keySort={sortKeys}
229236
defaultValue={defaultNewValue}
230237
showArrayIndices={showIndices}
231238
maxWidth="min(650px, 90vw)"
232239
className="block-shadow"
233-
stringTruncate={80}
240+
stringTruncate={90}
241+
customNodeDefinitions={demoData[selectedData]?.customNodeDefinitions}
234242
/>
235243
<VStack w="100%" align="flex-end" gap={4}>
236244
<HStack w="100%" justify="space-between" mt={4}>
@@ -281,6 +289,20 @@ function App() {
281289
<VStack backgroundColor="#f6f6f6" borderRadius={10} className="block-shadow">
282290
<FormControl>
283291
<VStack align="flex-start" m={4}>
292+
<HStack className="inputRow">
293+
<FormLabel className="labelWidth" textAlign="right">
294+
Demo data
295+
</FormLabel>
296+
<div className="inputWidth" style={{ flexGrow: 1 }}>
297+
<Select onChange={handleChangeData} value={selectedData}>
298+
{Object.entries(demoData).map(([key, { name }]) => (
299+
<option value={key} key={key}>
300+
{name}
301+
</option>
302+
))}
303+
</Select>
304+
</div>
305+
</HStack>
284306
<HStack className="inputRow">
285307
<FormLabel className="labelWidth" textAlign="right">
286308
Theme
@@ -297,20 +319,6 @@ function App() {
297319
</Select>
298320
</div>
299321
</HStack>
300-
<HStack className="inputRow">
301-
<FormLabel className="labelWidth" textAlign="right">
302-
Demo data
303-
</FormLabel>
304-
<div className="inputWidth" style={{ flexGrow: 1 }}>
305-
<Select onChange={handleChangeData} value={selectedData}>
306-
{Object.entries(demoData).map(([key, { name }]) => (
307-
<option value={key} key={key}>
308-
{name}
309-
</option>
310-
))}
311-
</Select>
312-
</div>
313-
</HStack>
314322
<HStack className="inputRow">
315323
<FormLabel className="labelWidth" textAlign="right">
316324
Data root name
@@ -357,6 +365,27 @@ function App() {
357365
</NumberInputStepper>
358366
</NumberInput>
359367
</HStack>
368+
<HStack className="inputRow">
369+
<FormLabel className="labelWidth" textAlign="right">
370+
Show counts
371+
</FormLabel>
372+
<div className="inputWidth" style={{ flexGrow: 1 }}>
373+
<Select
374+
onChange={(e) => setShowCount(e.target.value as 'Yes' | 'No' | 'When closed')}
375+
value={showCount}
376+
>
377+
<option value="Yes" key={0}>
378+
Yes
379+
</option>
380+
<option value="No" key={1}>
381+
No
382+
</option>
383+
<option value="When closed" key={2}>
384+
When closed
385+
</option>
386+
</Select>
387+
</div>
388+
</HStack>
360389
<CheckboxGroup colorScheme="primaryScheme">
361390
<Flex w="100%" justify="flex-start">
362391
<Checkbox

demo/src/data.tsx

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const data = {
3030
'Edit a value by clicking the "edit" icon, or double-clicking the value.',
3131
'You can change the type of any value',
3232
'You can add new values to objects or arrays',
33-
'You can edit individual values, or even a whole object node at once',
33+
'You can edit individual values, or even a whole object node at once (as JSON text)',
3434
{
3535
nested: 'An object inside an array',
3636
basic: false,
@@ -58,8 +58,8 @@ const data = {
5858
<Text>
5959
Note the additional editing restrictions in addition to the toggles above. This has been
6060
achieved by specifying filter functions for the <span className="code">restrictEdit</span>
61-
, <span className="code">restrictDelete</span> and{' '}
62-
<span className="code">restrictAdd</span> props.{' '}
61+
, <span className="code">restrictDelete</span>, <span className="code">restrictAdd</span>{' '}
62+
and <span className="code">restrictTypeSelection</span> props.{' '}
6363
<Link href="https://github.com/CarlosNZ/json-edit-react#readme" isExternal>
6464
Learn more
6565
</Link>
@@ -69,6 +69,7 @@ const data = {
6969
restrictEdit: ({ value }) => typeof value === 'object' && value !== null,
7070
restrictDelete: ({ value }) => typeof value === 'object' && value !== null,
7171
restrictAdd: ({ value }) => !Array.isArray(value),
72+
restrictTypeSelection: true,
7273
collapse: 1,
7374
data: {
7475
name: 'Luke Skywalker',
@@ -1783,6 +1784,7 @@ const data = {
17831784
node. Let's see how long it lasts... 😉
17841785
</Text>
17851786
),
1787+
rootName: 'liveData',
17861788
collapse: 3,
17871789
restrictEdit: ({ key, value, level, path, size }) => {
17881790
return level === 0 || key === 'messages' || key === 'lastEdited'
@@ -1811,12 +1813,94 @@ const data = {
18111813
</Text>
18121814
</Flex>
18131815
),
1816+
rootName: 'theme',
18141817
restrictEdit: ({ key, level }) => level === 0 || ['fragments', 'styles'].includes(key),
18151818
restrictDelete: ({ key }) => ['displayName', 'fragments', 'styles'].includes(key),
18161819
restrictAdd: ({ level }) => level === 0,
1820+
restrictTypeSelection: ['string', 'object', 'array'],
18171821
collapse: 2,
18181822
data: {},
18191823
},
1824+
customNodes: {
1825+
name: '🔧 Custom Nodes',
1826+
description: (
1827+
<Flex flexDir="column" gap={2}>
1828+
<Text>
1829+
This data set shows <strong>Custom Nodes</strong> — you can provide your own components to
1830+
present specialised data in a unique way, or provide a more complex editing mechanism for
1831+
a specialised data structure, say.
1832+
</Text>
1833+
<Text>
1834+
In this example, compare the raw JSON (edit the data root) with what is presented here.
1835+
</Text>
1836+
<Text>
1837+
See the{' '}
1838+
<a href="https://github.com/CarlosNZ/json-edit-react#custom-nodes">Custom Nodes</a>{' '}
1839+
section of the documentation for more info.
1840+
</Text>
1841+
</Flex>
1842+
),
1843+
rootName: 'Superheroes',
1844+
collapse: 2,
1845+
data: [
1846+
{
1847+
name: 'Steve Rogers',
1848+
aliases: ['Captain America', 'The First Avenger'],
1849+
logo: 'https://logos-world.net/wp-content/uploads/2023/05/Captain-America-Logo.png',
1850+
actor: 'Chris Evans',
1851+
publisher: 'Marvel',
1852+
},
1853+
{
1854+
name: 'Clark Kent',
1855+
aliases: ['Superman', 'Man of Steel', 'Son of Krypton'],
1856+
logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Superman_shield.svg/2560px-Superman_shield.svg.png',
1857+
actor: 'Henry Cavill',
1858+
publisher: 'D.C. Comics',
1859+
},
1860+
],
1861+
customNodeDefinitions: [
1862+
{
1863+
condition: ({ key, value }) =>
1864+
key === 'logo' &&
1865+
typeof value === 'string' &&
1866+
value.startsWith('http') &&
1867+
value.endsWith('.png'),
1868+
element: ({ data }) => {
1869+
const truncate = (string: string, length = 50) =>
1870+
string.length < length ? string : `${string.slice(0, length - 2).trim()}...`
1871+
return (
1872+
<div style={{ maxWidth: 250 }}>
1873+
<a href={data} target="_blank">
1874+
<img src={data} style={{ maxHeight: 75 }} />
1875+
<p style={{ fontSize: '0.75em' }}>{truncate(data)}</p>{' '}
1876+
</a>
1877+
</div>
1878+
)
1879+
},
1880+
},
1881+
{
1882+
condition: ({ key }) => key === 'publisher',
1883+
element: ({ data }) => {
1884+
return (
1885+
<p
1886+
style={{
1887+
padding: '0.5em 1em',
1888+
border: '2px solid red',
1889+
borderRadius: '1.5em',
1890+
backgroundColor: 'yellow',
1891+
marginTop: '0.5em',
1892+
marginRight: '1em',
1893+
fontFamily: 'sans-serif',
1894+
}}
1895+
>
1896+
Presented by: <strong>{data}</strong>
1897+
</p>
1898+
)
1899+
},
1900+
hideKey: true,
1901+
},
1902+
],
1903+
},
18201904
}
18211905

18221906
export default data

demo/watch.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ console.log('Relaunching...')
55

66
execSync('cp -R ../src/* ./src/json-edit-react/src')
77
execSync('cp ../package.json ./src/json-edit-react')
8+
9+
// Copy to fig tree repo (temporary)
10+
// execSync('cp -R ../src/* ../../fig-tree-evaluator/expression-builder/src/json-edit-react')

0 commit comments

Comments
 (0)