Skip to content

Commit 3aae3d9

Browse files
authored
Custom component library -- initial setup (#191)
* Scaffolding for component library * Handle Date values * Improve types * Couple more components * Add post-publish install script * Error handling * Start README
1 parent 8b5097a commit 3aae3d9

36 files changed

+2100
-16
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ temp.js
4646
.vscode/settings.json
4747
demo/src/firebaseConfig.json
4848

49-
# Built package in demo
49+
# Built package in demo/custom library
5050
demo/src/package
51+
custom-component-library/components/package
5152
.original-readme.md
5253
.npm-readme.md
5354
demo/src/imports/import.ts
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

custom-component-library/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
A collection of [Custom Components](https://github.com/CarlosNZ/json-edit-react#custom-nodes) for **json-edit-react**.
2+
3+
A work in progress.
4+
5+
Contains a [Vite](https://vite.dev/) web-app for previewing and developing components.
6+
7+
The individual components are in the `/components` folder, along with demo data (in `data.ts`).
8+
9+
## Components
10+
11+
These are the ones I'm planning for now:
12+
13+
- [x] Hyperlink/URL
14+
- [x] Undefined
15+
- [x] Date Object
16+
- [ ] Date Picker (with ISO string)
17+
- [ ] `NaN`
18+
- [ ] BigInt
19+
20+
## Development
21+
22+
From within this folder: `/custom-component-library`:
23+
24+
Install dependencies:
25+
26+
```js
27+
yarn install
28+
```
29+
30+
Launch app:
31+
32+
```js
33+
yarn dev
34+
```
35+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React, { useRef } from 'react'
2+
import { StringDisplay, toPathString, StringEdit, type CustomNodeProps } from 'json-edit-react'
3+
4+
export const DateObjectCustomComponent: React.FC<CustomNodeProps<unknown>> = (props) => {
5+
const { nodeData, isEditing, setValue, getStyles, canEdit, value, handleEdit, onError } = props
6+
const lastValidDate = useRef(value)
7+
8+
if (value instanceof Date) lastValidDate.current = value
9+
10+
return isEditing ? (
11+
<StringEdit
12+
styles={getStyles('input', nodeData)}
13+
pathString={toPathString(nodeData.path)}
14+
{...props}
15+
value={value instanceof Date ? value.toISOString() : (value as string)}
16+
setValue={setValue as React.Dispatch<React.SetStateAction<string>>}
17+
handleEdit={() => {
18+
const newDate = new Date(value as string)
19+
try {
20+
// Check if date is valid by trying to convert to ISO
21+
newDate.toISOString()
22+
handleEdit(newDate)
23+
} catch {
24+
handleEdit(lastValidDate.current)
25+
onError({ code: 'UPDATE_ERROR', message: 'Invalid Date' }, value)
26+
}
27+
}}
28+
/>
29+
) : (
30+
<StringDisplay
31+
{...props}
32+
styles={getStyles('string', nodeData)}
33+
canEdit={canEdit}
34+
pathString={toPathString(nodeData.path)}
35+
value={nodeData.value.toLocaleString()}
36+
/>
37+
)
38+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { DateObjectCustomComponent } from './component'
2+
import { type CustomNodeDefinition } from 'json-edit-react'
3+
4+
export const DateObjectDefinition: CustomNodeDefinition = {
5+
condition: (nodeData) => nodeData.value instanceof Date,
6+
element: DateObjectCustomComponent,
7+
showEditTools: true,
8+
showOnEdit: true,
9+
name: 'Date Object', // shown in the Type selector menu
10+
showInTypesSelector: true,
11+
defaultValue: new Date(),
12+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './definition'
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* An URL display Custom Component
3+
*
4+
* A simple custom node which detects urls in data and makes them active
5+
* hyperlinks.
6+
*/
7+
8+
import React from 'react'
9+
import { toPathString, type CustomNodeProps, StringDisplay } from 'json-edit-react'
10+
11+
export const LinkCustomComponent: React.FC<
12+
CustomNodeProps<{ linkStyles?: React.CSSProperties; stringTruncate?: number }>
13+
> = (props) => {
14+
const { setIsEditing, getStyles, nodeData, customNodeProps = {} } = props
15+
const styles = getStyles('string', nodeData)
16+
const { linkStyles = { fontWeight: 'bold', textDecoration: 'underline' }, stringTruncate = 60 } =
17+
customNodeProps
18+
return (
19+
<div
20+
onClick={(e) => {
21+
if (e.getModifierState('Control') || e.getModifierState('Meta')) setIsEditing(true)
22+
}}
23+
style={styles}
24+
>
25+
<StringDisplay
26+
{...props}
27+
pathString={toPathString(nodeData.path)}
28+
styles={{ ...styles }}
29+
value={nodeData.value as string}
30+
stringTruncate={stringTruncate}
31+
TextWrapper={({ children }) => {
32+
return (
33+
<a
34+
href={nodeData.value as string}
35+
target="_blank"
36+
rel="noreferrer"
37+
style={{ ...styles, ...linkStyles, cursor: 'pointer' }}
38+
>
39+
{children}
40+
</a>
41+
)
42+
}}
43+
/>
44+
</div>
45+
)
46+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { type CustomNodeDefinition } from 'json-edit-react'
2+
import { LinkCustomComponent } from './component'
3+
4+
export const LinkCustomNodeDefinition: CustomNodeDefinition<{
5+
linkStyles?: React.CSSProperties
6+
stringTruncate?: number
7+
}> = {
8+
// Condition is a regex to match url strings
9+
condition: ({ value }) => typeof value === 'string' && /^https?:\/\/.+\..+$/.test(value),
10+
element: LinkCustomComponent,
11+
// customNodeProps: { stringTruncate: 80 },
12+
showOnView: true,
13+
showOnEdit: false,
14+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './definition'
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from 'react'
2+
import { type CustomNodeProps } from 'json-edit-react'
3+
4+
export const UndefinedCustomComponent: React.FC<CustomNodeProps<unknown>> = () => (
5+
<div style={{ fontStyle: 'italic', color: '#9b9b9b' }}>undefined</div>
6+
)

0 commit comments

Comments
 (0)