Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions custom-component-library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ These are the ones currently available:
- [x] `BigInt`
- [x] Markdown
- [x] "Enhanced" link
- [ ] Image (to-do)
- [x] Image
- [ ] Colour picker (to-do)

## Development

Expand Down Expand Up @@ -59,7 +60,7 @@ Custom components should consider the following:
- `Tab`/`Shift-Tab` to navigate
- `Enter` to submit
- `Escape` to cancel
- Provide customisation options, particularly styles
- Provide customisation options, particularly styles (but make sure to specify defaults)
- If the data contains non-JSON types, add a "stringify" and "reviver" function definition (see `BigInt`, `NaN` and `Symbol` components)

If your custom component is "string-like", there are two helper components exported with the package: `StringDisplay` and `StringEdit` -- these are the same components used for the actual "string" elements in the main package. See the [Hyperlink](https://github.com/CarlosNZ/json-edit-react/blob/main/custom-component-library/components/Hyperlink/component.tsx) and [BigInt](https://github.com/CarlosNZ/json-edit-react/blob/main/custom-component-library/components/BigInt/component.tsx) components for examples of how to use them.
Expand Down
24 changes: 24 additions & 0 deletions custom-component-library/components/Image/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* An Image display Custom Component
*/

import React from 'react'
import { type CustomNodeProps } from '@json-edit-react'

export interface ImageProps {
imageStyles?: React.CSSProperties
altText?: string
}

export const ImageComponent: React.FC<CustomNodeProps<ImageProps>> = (props) => {
const { value, customNodeProps = {} } = props

const { imageStyles = { maxWidth: 200, maxHeight: 200 }, altText = value as string } =
customNodeProps

return (
<a href={value as string} target="_blank" rel="noreferrer">
<img src={value as string} title={altText} style={imageStyles} alt={altText} />
</a>
)
}
13 changes: 13 additions & 0 deletions custom-component-library/components/Image/definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { type CustomNodeDefinition } from '@json-edit-react'
import { ImageComponent, ImageProps } from './component'

const imageLinkRegex = /^https?:\/\/[^\s]+?\.(?:jpe?g|png|svg|gif)/i

export const ImageNodeDefinition: CustomNodeDefinition<ImageProps> = {
condition: ({ value }) => typeof value === 'string' && imageLinkRegex.test(value),
element: ImageComponent,
// customNodeProps: {},
showOnView: true,
showOnEdit: false,
name: 'Image',
}
1 change: 1 addition & 0 deletions custom-component-library/components/Image/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './definition'
1 change: 1 addition & 0 deletions custom-component-library/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './NaN'
export * from './Symbol'
export * from './BigInt'
export * from './Markdown'
export * from './Image'
24 changes: 16 additions & 8 deletions custom-component-library/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import {
BigIntDefinition,
MarkdownNodeDefinition,
EnhancedLinkCustomNodeDefinition,
ImageNodeDefinition,
} from '../components'
import { testData } from './data'
import { JsonData, JsonEditor } from '@json-edit-react'
import { JsonEditor } from '@json-edit-react'

if (testData?.['Date & Time']) {
// @ts-expect-error redefine after initialisation
Expand All @@ -33,25 +34,32 @@ if (testData?.['Date & Time']) {
type TestData = typeof testData

function App() {
const [data, setData] = useState<JsonData>(testData)
const [data, setData] = useState<TestData>(testData)

console.log('Current data', data)

// Properties that are conditional on some data property:

// Display the time depending on whether or not the "Show time" toggle is
// checked
const showTime = data?.['Date & Time']?.['Show Time in Date?'] ?? false

// Image sizing
const maxWidth = data?.Images?.['Image properties']?.maxWidth
const maxHeight = data?.Images?.['Image properties']?.maxHeight

return (
<div id="container">
<JsonEditor
// restrictEdit={true}
data={data}
setData={setData}
setData={setData as (data: unknown) => void}
customNodeDefinitions={[
{ ...ImageNodeDefinition, customNodeProps: { imageStyles: { maxWidth, maxHeight } } },
LinkCustomNodeDefinition,
{
...(STORE_DATE_AS_DATE_OBJECT ? DateObjectDefinition : DatePickerDefinition),
customNodeProps: {
// Display the time depending on whether or not the "Show time"
// toggle is checked
showTime: (data as TestData)?.['Date & Time']?.['Show Time in Date?'] ?? false,
},
customNodeProps: { showTime },
},
EnhancedLinkCustomNodeDefinition,
UndefinedDefinition,
Expand Down
10 changes: 10 additions & 0 deletions custom-component-library/src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const testData = {
- BooleanToggle
- NaN
- Symbol
- Image

Click [here](https://github.com/CarlosNZ/json-edit-react/blob/main/custom-component-library/README.md) for more info
`,
Expand Down Expand Up @@ -48,4 +49,13 @@ export const testData = {
},
Markdown:
'Uses [react-markdown](https://www.npmjs.com/package/react-markdown) to render **Markdown** *text content*. ',
Images: {
JPG: 'https://film-grab.com/wp-content/uploads/2014/07/51.jpg',
PNG: 'https://github.com/CarlosNZ/json-edit-react/blob/main/image/logo192.png?raw=true',
GIF: 'https://media2.giphy.com/media/v1.Y2lkPTc5MGI3NjExdnV0aHB0c2xiMHFmdGY3Z2NkenBkb3Rmd3hvdTlkaTlkNGYxOXFtOSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/9E7kUhnT9eDok/giphy.gif',
'Image properties': {
maxWidth: 200,
maxHeight: 100,
},
},
}
16 changes: 7 additions & 9 deletions demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ import { ArrowBackIcon, ArrowForwardIcon, InfoIcon } from '@chakra-ui/icons'
import { demoDataDefinitions } from './demoData'
import { useDatabase } from './useDatabase'
import './style.css'
import { getLineHeight, truncate } from './helpers'
import { getConditionalDefinitions, getLineHeight, truncate } from './helpers'
import { Loading } from '../../custom-component-library/components/_common/Loading'
import { testData } from '../../custom-component-library/src/data'

const CodeEditor = lazy(() => import('./CodeEditor'))
const SourceIndicator = lazy(() => import('./SourceIndicator'))
Expand Down Expand Up @@ -155,14 +156,11 @@ function App() {
// }, [])

const customNodeDefinitions =
selectedDataSet === 'customComponentLibrary' &&
typeof data === 'object' &&
(data as Record<string, Record<string, unknown>>)?.['Date & Time']?.['Show Time in Date?'] &&
Array.isArray(dataDefinition.customNodeDefinitions)
? [
{ ...dataDefinition.customNodeDefinitions[0], customNodeProps: { showTime: true } },
...dataDefinition.customNodeDefinitions.slice(1),
]
selectedDataSet === 'customComponentLibrary'
? getConditionalDefinitions(
data as typeof testData,
dataDefinition?.customNodeDefinitions ?? []
)
: dataDefinition.customNodeDefinitions

const updateState = (patch: Partial<AppState>) => setState({ ...state, ...patch })
Expand Down
4 changes: 3 additions & 1 deletion demo/src/demoData/dataDefinitions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
BigIntDefinition,
MarkdownNodeDefinition,
EnhancedLinkCustomNodeDefinition,
ImageNodeDefinition,
} from '../../../custom-component-library/components'
import { testData } from '../../../custom-component-library/src/data'
import {
Expand Down Expand Up @@ -831,14 +832,15 @@ export const demoDataDefinitions: Record<string, DemoData> = {
</Flex>
),
rootName: 'components',
collapse: 2,
collapse: 3,
data: testData,
customNodeDefinitions: [
// Must keep this one first as we override it by index in App.tsx
{
...DateObjectDefinition,
customNodeProps: { showTime: false },
},
ImageNodeDefinition,
LinkCustomNodeDefinition,
EnhancedLinkCustomNodeDefinition,
UndefinedDefinition,
Expand Down
30 changes: 29 additions & 1 deletion demo/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { JsonData } from '@json-edit-react'
import { JsonData, type CustomNodeDefinition } from '@json-edit-react'
import { testData } from '../../custom-component-library/src/data'

export const truncate = (string: string, length = 200) =>
string.length < length ? string : `${string.slice(0, length - 2).trim()}...`
Expand All @@ -20,3 +21,30 @@ const jsonStringify = (data: JsonData) =>
},
2
)

// For the "CustomNodeLibrary" data, returns modified definitions dependent on
// the data
export const getConditionalDefinitions = (
data: typeof testData,
customNodeDefinitions: CustomNodeDefinition[]
) =>
customNodeDefinitions.map((definition) => {
if (definition?.name === 'Image')
return {
...definition,
customNodeProps: {
imageStyles: {
maxHeight: data?.Images?.['Image properties']?.maxHeight,
maxWidth: data?.Images?.['Image properties']?.maxWidth,
},
},
}

if (definition?.name === 'Date Object')
return {
...definition,
customNodeProps: { showTime: data?.['Date & Time']?.['Show Time in Date?'] ?? false },
}

return definition
})