Skip to content

#109 #95 Enum types and Object key options #183

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

Merged
merged 16 commits into from
Mar 26, 2025
631 changes: 444 additions & 187 deletions README.md

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions README_npm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- The README that will appear on the package's NPM page: https://www.npmjs.com/package/json-edit-react
On publish, this file is temporarily renamed to the main README so it gets published to npm,
then renamed back to this file, so the primary README remains published to Github repo.

The {{BLOCKS}} below are replaced from the equivalent blocks in the main README file when the
`yarn prepareReadme` script is run (which also happens before publish).
-->

{{NPM INTRO}}

{{NPM USAGE}}

---

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)
30 changes: 28 additions & 2 deletions demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ function App() {
const previousTheme = useRef<Theme>() // Used when resetting after theme editing
const toast = useToast()

const [isSearchFocused, setIsSearchFocused] = useState(false)

const { liveData, loading, updateLiveData } = useDatabase()

const [
Expand Down Expand Up @@ -331,18 +333,24 @@ function App() {
<Box position="relative">
<Input
id="searchTextInput"
placeholder={dataDefinition.searchPlaceholder ?? 'Search values'}
placeholder={
isSearchFocused ? dataDefinition.searchPlaceholder ?? 'Search values' : '🔍'
}
onFocus={() => setIsSearchFocused(true)}
onBlur={() => setIsSearchFocused(false)}
bgColor={'#f6f6f6'}
borderColor="gainsboro"
borderRadius={50}
size="sm"
w={60}
w={20}
value={searchText}
onChange={(e) => updateState({ searchText: e.target.value })}
position="absolute"
right={2}
top={2}
zIndex={100}
_focus={{ w: '45%' }}
transition={'width 0.3s'}
/>
<JsonEditor
data={data}
Expand Down Expand Up @@ -408,6 +416,23 @@ function App() {
restrictDelete={restrictDelete}
restrictAdd={restrictAdd}
restrictTypeSelection={dataDefinition?.restrictTypeSelection}
// restrictTypeSelection={[
// 'string',
// 'number',
// 'boolean',
// 'null',
// { enum: 'Option', values: ['One', 'Two', 'Three'] },
// {
// enum: 'Hobby',
// values: ['partying', 'building stuff', 'avenging', 'time travel'],
// matchPriority: 1,
// },
// {
// enum: 'Other activities that could be quite long',
// values: ['changing', 'building stuff', 'avenging', 'money money money money'],
// matchPriority: 2,
// },
// ]}
restrictDrag={false}
searchFilter={dataDefinition?.searchFilter}
searchText={searchText}
Expand All @@ -428,6 +453,7 @@ function App() {
// : false
// }
defaultValue={dataDefinition?.defaultValue ?? defaultNewValue}
newKeyOptions={dataDefinition?.newKeyOptions}
showArrayIndices={showIndices}
showStringQuotes={showStringQuotes}
minWidth={'min(500px, 95vw)'}
Expand Down
4 changes: 3 additions & 1 deletion demo/src/demoData/data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ export const data: Record<string, object> = {
number: 99,
boolean: true,
nothing: null,
enum: 'Option B',
Usage: [
'Edit a value by clicking the "edit" icon, or double-clicking the value.',
'You can change the type of any value',
'You can add new values to objects or arrays',
'You can edit individual values, or even a whole object node at once (as JSON text)',
'You can also drag and drop! 🆕',
'You can also drag and drop!',
{
nested: 'An object inside an array',
basic: false,
Expand Down Expand Up @@ -1700,6 +1701,7 @@ export const data: Record<string, object> = {
postalCode: '90265',
},
hobbies: ['partying', 'building stuff', 'avenging'],
category: 'human',
},
customNodes: [
{
Expand Down
135 changes: 132 additions & 3 deletions demo/src/demoData/dataDefinitions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ import {
matchNode,
} from '../_imports'
import {
DataType,
DefaultValueFunction,
ErrorString,
NewKeyOptionsFunction,
OnChangeFunction,
OnErrorFunction,
SearchFilterFunction,
standardDataTypes,
ThemeStyles,
TypeFilterFunction,
TypeOptions,
UpdateFunction,
UpdateFunctionProps,
} from '../json-edit-react/src/types'
Expand All @@ -39,7 +42,7 @@ export interface DemoData {
restrictEdit?: boolean | FilterFunction
restrictDelete?: boolean | FilterFunction
restrictAdd?: boolean | FilterFunction
restrictTypeSelection?: boolean | DataType[]
restrictTypeSelection?: boolean | TypeOptions | TypeFilterFunction
searchFilter?: 'key' | 'value' | 'all' | SearchFilterFunction
searchPlaceholder?: string
onUpdate?: (
Expand All @@ -52,6 +55,7 @@ export interface DemoData {
onError?: OnErrorFunction
showErrorMessages?: boolean
defaultValue?: unknown | DefaultValueFunction
newKeyOptions?: string[] | NewKeyOptionsFunction
customNodeDefinitions?: CustomNodeDefinition[]
customTextDefinitions?: CustomTextDefinitions
styles?: Partial<ThemeStyles>
Expand Down Expand Up @@ -94,6 +98,19 @@ export const demoDataDefinitions: Record<string, DemoData> = {
customNodeDefinitions: [dateNodeDefinition],
// restrictEdit: ({ key }) => key === 'number',
customTextEditorAvailable: true,
restrictTypeSelection: ({ key }) => {
if (key === 'enum')
return [
...standardDataTypes,
'Date',
{
enum: 'Custom Type',
values: ['Option A', 'Option B', 'Option C'],
matchPriority: 1,
},
]
return false
},
},
starWars: {
name: '🚀 Star Wars',
Expand Down Expand Up @@ -128,7 +145,82 @@ export const demoDataDefinitions: Record<string, DemoData> = {
restrictEdit: ({ value }) => typeof value === 'object' && value !== null,
restrictDelete: ({ value }) => typeof value === 'object' && value !== null,
restrictAdd: ({ value }) => !Array.isArray(value),
restrictTypeSelection: true,
restrictTypeSelection: ({ key, path }) => {
if (path.slice(-2)[0] === 'films' || (path.slice(-3)[0] === 'films' && key === 'title'))
return [
{
enum: 'Film',
values: [
'A New Hope',
'The Empire Strikes Back',
'Return of the Jedi',
'The Phantom Menace',
'Attack of the Clones',
'Revenge of the Sith',
'The Force Awakens',
'The Last Jedi',
'The Rise of Skywalker',
],
matchPriority: 1,
},
]
if (key === 'eye_color')
return [
{
enum: 'Eye colour',
values: [
'blue',
'brown',
'green',
'hazel',
'red',
'yellow',
'black',
'white',
'orange',
'pink',
'purple',
'grey',
'gold',
'unknown',
],
matchPriority: 1,
},
]
if (key === 'hair_color')
return [
{
enum: 'Hair colour',
values: ['black', 'blond', 'brown', 'auburn', 'grey', 'white', 'unknown'],
matchPriority: 1,
},
]
if (key === 'skin_color')
return [
{
enum: 'Skin colour',
values: [
'fair',
'brown',
'dark',
'gold',
'white',
'blue',
'red',
'yellow',
'green',
'pale',
'metal',
'orange',
'grey',
'mottled',
'unknown',
],
matchPriority: 1,
},
]
return true
},
collapse: 1,
customNodeDefinitions: [dateNodeDefinition, LinkCustomNodeDefinition],
data: data.starWars,
Expand Down Expand Up @@ -261,6 +353,11 @@ export const demoDataDefinitions: Record<string, DemoData> = {
this one.
</Link>
</Text>
<Text>
Also, notice if you try to add additional keys to the{' '}
<span className="code">address</span> field or the root node, you'll be limited to allowed
options via a drop-down.
</Text>
</Flex>
),
rootName: 'data',
Expand All @@ -283,6 +380,38 @@ export const demoDataDefinitions: Record<string, DemoData> = {
return 'JSON Schema error'
}
},
restrictTypeSelection: ({ key }) => {
if (key === 'category')
return [
...standardDataTypes,
{
enum: 'Category',
values: ['human', 'enhanced human', 'extra-terrestrial'],
matchPriority: 1,
},
]
return false
},
newKeyOptions: ({ key }) => {
if (key === 'data') return ['name', 'age', 'address', 'hobbies', 'category', 'isAlive']
if (key === 'address') return ['street', 'suburb', 'city', 'state', 'postalCode', 'country']
},
defaultValue: ({ key }, newKey) => {
if (key === 'hobbies') return 'Enter a hobby'

if (newKey === 'country') return 'United States'
if (newKey === 'suburb') return 'Enter a suburb'
if (newKey === 'category') return 'human'
if (newKey === 'isAlive') return true
if (newKey === 'hobbies') return ['avenging', '...add more']
if (newKey === 'address')
return {
street: 'Enter street address',
city: 'City',
state: 'CA',
postalCode: '12345',
}
},
customTextEditorAvailable: true,
},
liveData: {
Expand Down
17 changes: 16 additions & 1 deletion demo/src/demoData/jsonSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"street": {
"type": "string"
},
"suburb": {
"type": "string"
},
"city": {
"type": "string"
},
Expand All @@ -25,16 +28,28 @@
"postalCode": {
"type": "string",
"pattern": "\\d{5}"
},
"country": {
"type": "string"
}
},
"required": ["street", "city", "state", "postalCode"]
"required": ["street", "city", "state", "postalCode"],
"additionalProperties": false
},
"hobbies": {
"type": "array",
"items": {
"type": "string"
}
},
"category": {
"type": "string",
"enum": ["human", "enhanced human", "extra-terrestrial"]
},
"isAlive": {
"type": "boolean"
}
},
"additionalProperties": false,
"required": ["name", "age"]
}
2 changes: 1 addition & 1 deletion demo/src/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const version = '1.23.1'
export const version = '1.25.0-beta1'
Binary file added image/enum_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/key_select.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
"build": "rimraf ./build && rollup -c && rimraf ./build/dts",
"lint": "npx eslint \"src/**\"",
"postbuild": "node ./scripts/cleanBuildTypes.cjs",
"prepareReadme": "python3 scripts/github-to-npm-markdown.py README.md -o .npm-readme.md",
"prepublishOnly": "yarn build && npm run prepareReadme && python3 scripts/use-npm-readme.py prepare",
"postpublish": "python3 scripts/use-npm-readme.py restore",
"prepareReadme": "python3 scripts/build_npm_readme.py",
"prepublishOnly": "yarn build && yarn prepareReadme && python3 scripts/use_npm_readme.py prepare",
"postpublish": "python3 scripts/use_npm_readme.py restore",
"compile": "rimraf ./build && tsc",
"release": "yarn publish",
"release-demo": "cd demo && yarn deploy"
Expand Down
Loading