Skip to content

Commit c1c54ae

Browse files
authored
#109 #95 Enum types and Object key options (#183)
* Implement Enum types * Implement key options * Improve search layout, demo data definitions * Update README and publish scripts
1 parent 484591e commit c1c54ae

24 files changed

+1056
-478
lines changed

README.md

+444-187
Large diffs are not rendered by default.

README_npm.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- The README that will appear on the package's NPM page: https://www.npmjs.com/package/json-edit-react
2+
On publish, this file is temporarily renamed to the main README so it gets published to npm,
3+
then renamed back to this file, so the primary README remains published to Github repo.
4+
5+
The {{BLOCKS}} below are replaced from the equivalent blocks in the main README file when the
6+
`yarn prepareReadme` script is run (which also happens before publish).
7+
-->
8+
9+
{{NPM INTRO}}
10+
11+
{{NPM USAGE}}
12+
13+
---
14+
15+
For **FULL DOCUMENTATION**, visit [https://github.com/CarlosNZ/json-edit-react](https://github.com/CarlosNZ/json-edit-react?tab=readme-ov-file#json-edit-react)

demo/src/App.tsx

+28-2
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ function App() {
122122
const previousTheme = useRef<Theme>() // Used when resetting after theme editing
123123
const toast = useToast()
124124

125+
const [isSearchFocused, setIsSearchFocused] = useState(false)
126+
125127
const { liveData, loading, updateLiveData } = useDatabase()
126128

127129
const [
@@ -331,18 +333,24 @@ function App() {
331333
<Box position="relative">
332334
<Input
333335
id="searchTextInput"
334-
placeholder={dataDefinition.searchPlaceholder ?? 'Search values'}
336+
placeholder={
337+
isSearchFocused ? dataDefinition.searchPlaceholder ?? 'Search values' : '🔍'
338+
}
339+
onFocus={() => setIsSearchFocused(true)}
340+
onBlur={() => setIsSearchFocused(false)}
335341
bgColor={'#f6f6f6'}
336342
borderColor="gainsboro"
337343
borderRadius={50}
338344
size="sm"
339-
w={60}
345+
w={20}
340346
value={searchText}
341347
onChange={(e) => updateState({ searchText: e.target.value })}
342348
position="absolute"
343349
right={2}
344350
top={2}
345351
zIndex={100}
352+
_focus={{ w: '45%' }}
353+
transition={'width 0.3s'}
346354
/>
347355
<JsonEditor
348356
data={data}
@@ -408,6 +416,23 @@ function App() {
408416
restrictDelete={restrictDelete}
409417
restrictAdd={restrictAdd}
410418
restrictTypeSelection={dataDefinition?.restrictTypeSelection}
419+
// restrictTypeSelection={[
420+
// 'string',
421+
// 'number',
422+
// 'boolean',
423+
// 'null',
424+
// { enum: 'Option', values: ['One', 'Two', 'Three'] },
425+
// {
426+
// enum: 'Hobby',
427+
// values: ['partying', 'building stuff', 'avenging', 'time travel'],
428+
// matchPriority: 1,
429+
// },
430+
// {
431+
// enum: 'Other activities that could be quite long',
432+
// values: ['changing', 'building stuff', 'avenging', 'money money money money'],
433+
// matchPriority: 2,
434+
// },
435+
// ]}
411436
restrictDrag={false}
412437
searchFilter={dataDefinition?.searchFilter}
413438
searchText={searchText}
@@ -428,6 +453,7 @@ function App() {
428453
// : false
429454
// }
430455
defaultValue={dataDefinition?.defaultValue ?? defaultNewValue}
456+
newKeyOptions={dataDefinition?.newKeyOptions}
431457
showArrayIndices={showIndices}
432458
showStringQuotes={showStringQuotes}
433459
minWidth={'min(500px, 95vw)'}

demo/src/demoData/data.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ export const data: Record<string, object> = {
44
number: 99,
55
boolean: true,
66
nothing: null,
7+
enum: 'Option B',
78
Usage: [
89
'Edit a value by clicking the "edit" icon, or double-clicking the value.',
910
'You can change the type of any value',
1011
'You can add new values to objects or arrays',
1112
'You can edit individual values, or even a whole object node at once (as JSON text)',
12-
'You can also drag and drop! 🆕',
13+
'You can also drag and drop!',
1314
{
1415
nested: 'An object inside an array',
1516
basic: false,
@@ -1700,6 +1701,7 @@ export const data: Record<string, object> = {
17001701
postalCode: '90265',
17011702
},
17021703
hobbies: ['partying', 'building stuff', 'avenging'],
1704+
category: 'human',
17031705
},
17041706
customNodes: [
17051707
{

demo/src/demoData/dataDefinitions.tsx

+132-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ import {
1111
matchNode,
1212
} from '../_imports'
1313
import {
14-
DataType,
1514
DefaultValueFunction,
1615
ErrorString,
16+
NewKeyOptionsFunction,
1717
OnChangeFunction,
1818
OnErrorFunction,
1919
SearchFilterFunction,
20+
standardDataTypes,
2021
ThemeStyles,
22+
TypeFilterFunction,
23+
TypeOptions,
2124
UpdateFunction,
2225
UpdateFunctionProps,
2326
} from '../json-edit-react/src/types'
@@ -39,7 +42,7 @@ export interface DemoData {
3942
restrictEdit?: boolean | FilterFunction
4043
restrictDelete?: boolean | FilterFunction
4144
restrictAdd?: boolean | FilterFunction
42-
restrictTypeSelection?: boolean | DataType[]
45+
restrictTypeSelection?: boolean | TypeOptions | TypeFilterFunction
4346
searchFilter?: 'key' | 'value' | 'all' | SearchFilterFunction
4447
searchPlaceholder?: string
4548
onUpdate?: (
@@ -52,6 +55,7 @@ export interface DemoData {
5255
onError?: OnErrorFunction
5356
showErrorMessages?: boolean
5457
defaultValue?: unknown | DefaultValueFunction
58+
newKeyOptions?: string[] | NewKeyOptionsFunction
5559
customNodeDefinitions?: CustomNodeDefinition[]
5660
customTextDefinitions?: CustomTextDefinitions
5761
styles?: Partial<ThemeStyles>
@@ -94,6 +98,19 @@ export const demoDataDefinitions: Record<string, DemoData> = {
9498
customNodeDefinitions: [dateNodeDefinition],
9599
// restrictEdit: ({ key }) => key === 'number',
96100
customTextEditorAvailable: true,
101+
restrictTypeSelection: ({ key }) => {
102+
if (key === 'enum')
103+
return [
104+
...standardDataTypes,
105+
'Date',
106+
{
107+
enum: 'Custom Type',
108+
values: ['Option A', 'Option B', 'Option C'],
109+
matchPriority: 1,
110+
},
111+
]
112+
return false
113+
},
97114
},
98115
starWars: {
99116
name: '🚀 Star Wars',
@@ -128,7 +145,82 @@ export const demoDataDefinitions: Record<string, DemoData> = {
128145
restrictEdit: ({ value }) => typeof value === 'object' && value !== null,
129146
restrictDelete: ({ value }) => typeof value === 'object' && value !== null,
130147
restrictAdd: ({ value }) => !Array.isArray(value),
131-
restrictTypeSelection: true,
148+
restrictTypeSelection: ({ key, path }) => {
149+
if (path.slice(-2)[0] === 'films' || (path.slice(-3)[0] === 'films' && key === 'title'))
150+
return [
151+
{
152+
enum: 'Film',
153+
values: [
154+
'A New Hope',
155+
'The Empire Strikes Back',
156+
'Return of the Jedi',
157+
'The Phantom Menace',
158+
'Attack of the Clones',
159+
'Revenge of the Sith',
160+
'The Force Awakens',
161+
'The Last Jedi',
162+
'The Rise of Skywalker',
163+
],
164+
matchPriority: 1,
165+
},
166+
]
167+
if (key === 'eye_color')
168+
return [
169+
{
170+
enum: 'Eye colour',
171+
values: [
172+
'blue',
173+
'brown',
174+
'green',
175+
'hazel',
176+
'red',
177+
'yellow',
178+
'black',
179+
'white',
180+
'orange',
181+
'pink',
182+
'purple',
183+
'grey',
184+
'gold',
185+
'unknown',
186+
],
187+
matchPriority: 1,
188+
},
189+
]
190+
if (key === 'hair_color')
191+
return [
192+
{
193+
enum: 'Hair colour',
194+
values: ['black', 'blond', 'brown', 'auburn', 'grey', 'white', 'unknown'],
195+
matchPriority: 1,
196+
},
197+
]
198+
if (key === 'skin_color')
199+
return [
200+
{
201+
enum: 'Skin colour',
202+
values: [
203+
'fair',
204+
'brown',
205+
'dark',
206+
'gold',
207+
'white',
208+
'blue',
209+
'red',
210+
'yellow',
211+
'green',
212+
'pale',
213+
'metal',
214+
'orange',
215+
'grey',
216+
'mottled',
217+
'unknown',
218+
],
219+
matchPriority: 1,
220+
},
221+
]
222+
return true
223+
},
132224
collapse: 1,
133225
customNodeDefinitions: [dateNodeDefinition, LinkCustomNodeDefinition],
134226
data: data.starWars,
@@ -261,6 +353,11 @@ export const demoDataDefinitions: Record<string, DemoData> = {
261353
this one.
262354
</Link>
263355
</Text>
356+
<Text>
357+
Also, notice if you try to add additional keys to the{' '}
358+
<span className="code">address</span> field or the root node, you'll be limited to allowed
359+
options via a drop-down.
360+
</Text>
264361
</Flex>
265362
),
266363
rootName: 'data',
@@ -283,6 +380,38 @@ export const demoDataDefinitions: Record<string, DemoData> = {
283380
return 'JSON Schema error'
284381
}
285382
},
383+
restrictTypeSelection: ({ key }) => {
384+
if (key === 'category')
385+
return [
386+
...standardDataTypes,
387+
{
388+
enum: 'Category',
389+
values: ['human', 'enhanced human', 'extra-terrestrial'],
390+
matchPriority: 1,
391+
},
392+
]
393+
return false
394+
},
395+
newKeyOptions: ({ key }) => {
396+
if (key === 'data') return ['name', 'age', 'address', 'hobbies', 'category', 'isAlive']
397+
if (key === 'address') return ['street', 'suburb', 'city', 'state', 'postalCode', 'country']
398+
},
399+
defaultValue: ({ key }, newKey) => {
400+
if (key === 'hobbies') return 'Enter a hobby'
401+
402+
if (newKey === 'country') return 'United States'
403+
if (newKey === 'suburb') return 'Enter a suburb'
404+
if (newKey === 'category') return 'human'
405+
if (newKey === 'isAlive') return true
406+
if (newKey === 'hobbies') return ['avenging', '...add more']
407+
if (newKey === 'address')
408+
return {
409+
street: 'Enter street address',
410+
city: 'City',
411+
state: 'CA',
412+
postalCode: '12345',
413+
}
414+
},
286415
customTextEditorAvailable: true,
287416
},
288417
liveData: {

demo/src/demoData/jsonSchema.json

+16-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
"street": {
1717
"type": "string"
1818
},
19+
"suburb": {
20+
"type": "string"
21+
},
1922
"city": {
2023
"type": "string"
2124
},
@@ -25,16 +28,28 @@
2528
"postalCode": {
2629
"type": "string",
2730
"pattern": "\\d{5}"
31+
},
32+
"country": {
33+
"type": "string"
2834
}
2935
},
30-
"required": ["street", "city", "state", "postalCode"]
36+
"required": ["street", "city", "state", "postalCode"],
37+
"additionalProperties": false
3138
},
3239
"hobbies": {
3340
"type": "array",
3441
"items": {
3542
"type": "string"
3643
}
44+
},
45+
"category": {
46+
"type": "string",
47+
"enum": ["human", "enhanced human", "extra-terrestrial"]
48+
},
49+
"isAlive": {
50+
"type": "boolean"
3751
}
3852
},
53+
"additionalProperties": false,
3954
"required": ["name", "age"]
4055
}

demo/src/version.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const version = '1.23.1'
1+
export const version = '1.25.0-beta1'

image/enum_example.png

300 KB
Loading

image/key_select.png

142 KB
Loading

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
"build": "rimraf ./build && rollup -c && rimraf ./build/dts",
2929
"lint": "npx eslint \"src/**\"",
3030
"postbuild": "node ./scripts/cleanBuildTypes.cjs",
31-
"prepareReadme": "python3 scripts/github-to-npm-markdown.py README.md -o .npm-readme.md",
32-
"prepublishOnly": "yarn build && npm run prepareReadme && python3 scripts/use-npm-readme.py prepare",
33-
"postpublish": "python3 scripts/use-npm-readme.py restore",
31+
"prepareReadme": "python3 scripts/build_npm_readme.py",
32+
"prepublishOnly": "yarn build && yarn prepareReadme && python3 scripts/use_npm_readme.py prepare",
33+
"postpublish": "python3 scripts/use_npm_readme.py restore",
3434
"compile": "rimraf ./build && tsc",
3535
"release": "yarn publish",
3636
"release-demo": "cd demo && yarn deploy"

0 commit comments

Comments
 (0)