Skip to content

Commit 0405b55

Browse files
authored
#180 Make original value node available to custom nodes (#181)
* Abstract KeyDisplay into single component * Hide colon via CSS if required * Update README.md * Remove dupe assignation
1 parent ae42a77 commit 0405b55

File tree

7 files changed

+53
-18
lines changed

7 files changed

+53
-18
lines changed

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,9 @@ Custom nodes are provided in the `customNodeDefinitions` prop, as an array of ob
662662
showOnView // boolean, default true
663663
showEditTools // boolean, default true
664664
name // string (appears in Types selector)
665-
showInTypesSelector, // boolean (optional), default false
665+
showInTypesSelector // boolean (optional), default false
666+
passOriginalNode // boolean (optional), default false -- if `true` makes the original node
667+
// for rendering within Custom Node
666668
667669
// Only affects Collection nodes:
668670
showCollectionWrapper // boolean (optional), default true
@@ -673,7 +675,7 @@ Custom nodes are provided in the `customNodeDefinitions` prop, as an array of ob
673675
674676
The `condition` is just a [Filter function](#filter-functions), with the same input parameters (`key`, `path`, `value`, etc.), and `element` is a React component. Every node in the data structure will be run through each condition function, and any that match will be replaced by your custom component. Note that if a node matches more than one custom definition conditions (from multiple components), the *first* one will be used, so place them in the array in priority order.
675677
676-
The component will receive *all* the same props as a standard node component plus some additional ones — see [BaseNodeProps](https://github.com/CarlosNZ/json-edit-react/blob/b085f6391dabf574809f1040b11401c13344923d/src/types.ts#L219-L265) (common to all nodes) and [CustomNodeProps](https://github.com/CarlosNZ/json-edit-react/blob/b085f6391dabf574809f1040b11401c13344923d/src/types.ts#L275-L287) type definitions. Specifically, if you want to update the data structure from your custom node, you'll need to call the `setValue` method on your node's data value.
678+
The component will receive *all* the same props as a standard node component plus some additional ones — see [BaseNodeProps](https://github.com/CarlosNZ/json-edit-react/blob/b085f6391dabf574809f1040b11401c13344923d/src/types.ts#L219-L265) (common to all nodes) and [CustomNodeProps](https://github.com/CarlosNZ/json-edit-react/blob/b085f6391dabf574809f1040b11401c13344923d/src/types.ts#L275-L287) type definitions. Specifically, if you want to update the data structure from your custom node, you'll need to call the `setValue` method on your node's data value. And if you enable `passOriginalNode` above, you'll also have access to `originalNode` and `originalNodeKey` in order to render the standard content (i.e. what would have been rendered if it wasn't intercepted by this Custom Node) -- this can be helpful if you want your Custom Node to just be the default content with a little extra decoration. (*Note:* you may need a little custom CSS to render these original node components identically to the default display.)
677679
678680
You can pass additional props specific to your component, if required, through the `customNodeProps` object. A thorough example of a custom **Date Picker** is used in the demo (along with a couple of other more basic presentational ones), which you can inspect to see how to utilise the standard props and a couple of custom props. View the source code [here](https://github.com/CarlosNZ/json-edit-react/blob/main/demo/src/customComponents/DateTimePicker.tsx).
679681
@@ -850,6 +852,8 @@ This component is heavily inspired by [react-json-view](https://github.com/mac-s
850852
851853
## Changelog
852854
855+
- **1.24.0**:
856+
- Option to access (and render) the original node (and its key) within a [Custom Node](#custom-nodes) ([#180](https://github.com/CarlosNZ/json-edit-react/issues/180))
853857
- **1.23.1**: Fix bug where you could collapse a node by clicking inside a "new key" input field [#175](https://github.com/CarlosNZ/json-edit-react/issues/175)
854858
- **1.23.0**:
855859
- Add `viewOnly` prop as a shorthand for restricting all editing [#168](https://github.com/CarlosNZ/json-edit-react/issues/168)

demo/src/App.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,29 @@ function App() {
415415
className="block-shadow"
416416
stringTruncate={90}
417417
customNodeDefinitions={dataDefinition?.customNodeDefinitions}
418+
// customNodeDefinitions={[
419+
// {
420+
// condition: ({ key }) => key === 'string',
421+
// element: ({ nodeData, value, originalNode, originalNodeKey }) => (
422+
// <div
423+
// style={{
424+
// display: 'flex',
425+
// // border: '1px solid red',
426+
// margin: '-0.5em',
427+
// alignItems: 'center',
428+
// }}
429+
// >
430+
// {originalNodeKey}
431+
// {/* {nodeData.key} */}
432+
// <span>ICON</span>:{' '}
433+
// <span style={{ lineHeight: 'unset !important' }}>{originalNode}</span>
434+
// </div>
435+
// ),
436+
// hideKey: true,
437+
// passOriginalNode: true,
438+
// showOnEdit: true,
439+
// },
440+
// ]}
418441
customText={dataDefinition?.customTextDefinitions}
419442
// icons={{ chevron: <IconCancel size="1.2em" /> }}
420443
// customButtons={[

src/CustomNode.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface CustomNodeData {
1313
showOnView?: boolean
1414
showEditTools?: boolean
1515
showCollectionWrapper?: boolean
16+
passOriginalNode?: boolean
1617
}
1718

1819
// Fetches matching custom nodes (based on condition filter) from custom node

src/KeyDisplay.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ export const KeyDisplay: React.FC<KeyDisplayProps> = ({
5454
{/* display "<empty string>" using pseudo class CSS */}
5555
</span>
5656
) : (
57-
`${name}:`
57+
`${name}`
5858
)}
59+
<span className="jer-key-colon">:</span>
5960
</span>
6061
)
6162

src/ValueNodeWrapper.tsx

+17-14
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export const ValueNodeWrapper: React.FC<ValueNodeProps> = (props) => {
112112
showEditTools = true,
113113
showOnEdit,
114114
showOnView,
115+
passOriginalNode,
115116
} = customNodeData
116117

117118
// Include custom node options in dataType list
@@ -256,6 +257,20 @@ export const ValueNodeWrapper: React.FC<ValueNodeProps> = (props) => {
256257
},
257258
}
258259

260+
const keyDisplayProps = {
261+
canEditKey,
262+
isEditingKey,
263+
pathString,
264+
path,
265+
name: name as string,
266+
handleKeyboard,
267+
handleEditKey,
268+
handleCancel,
269+
styles: getStyles('property', nodeData),
270+
getNextOrPrevious: (type: 'next' | 'prev') =>
271+
getNextOrPrevious(nodeData.fullData, path, type, sort),
272+
}
273+
259274
const ValueComponent = showCustomNode ? (
260275
<CustomNode
261276
{...props}
@@ -270,27 +285,15 @@ export const ValueNodeWrapper: React.FC<ValueNodeProps> = (props) => {
270285
isEditing={isEditing}
271286
setIsEditing={() => setCurrentlyEditingElement(path)}
272287
getStyles={getStyles}
288+
originalNode={passOriginalNode ? getInputComponent(data, inputProps) : undefined}
289+
originalNodeKey={passOriginalNode ? <KeyDisplay {...keyDisplayProps} /> : undefined}
273290
/>
274291
) : (
275292
// Need to re-fetch data type to make sure it's one of the "core" ones
276293
// when fetching a non-custom component
277294
getInputComponent(data, inputProps)
278295
)
279296

280-
const keyDisplayProps = {
281-
canEditKey,
282-
isEditingKey,
283-
pathString,
284-
path,
285-
name: name as string,
286-
handleKeyboard,
287-
handleEditKey,
288-
handleCancel,
289-
styles: getStyles('property', nodeData),
290-
getNextOrPrevious: (type: 'next' | 'prev') =>
291-
getNextOrPrevious(nodeData.fullData, path, type, sort),
292-
}
293-
294297
return (
295298
<div
296299
className="jer-component jer-value-component"

src/style.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ select:focus + .focus {
399399

400400
/* For displaying keys that are purely <empty string> "" */
401401
.jer-empty-string::after {
402-
content: '<empty string>:';
402+
content: '<empty string>';
403403
font-style: italic;
404404
font-size: 90%;
405405
}

src/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ export interface CustomNodeProps<T = Record<string, unknown>> extends BaseNodePr
285285
setIsEditing: React.Dispatch<React.SetStateAction<boolean>>
286286
getStyles: (element: ThemeableElement, nodeData: NodeData) => React.CSSProperties
287287
children?: JSX.Element | JSX.Element[] | null
288+
originalNode?: JSX.Element
289+
originalNodeKey?: JSX.Element
288290
}
289291

290292
export interface CustomNodeDefinition<T = Record<string, unknown>, U = Record<string, unknown>> {
@@ -298,6 +300,7 @@ export interface CustomNodeDefinition<T = Record<string, unknown>, U = Record<st
298300
showOnEdit?: boolean // default false
299301
showOnView?: boolean // default true
300302
showEditTools?: boolean // default true
303+
passOriginalNode?: boolean // default false
301304

302305
// For collection nodes only:
303306
showCollectionWrapper?: boolean // default true

0 commit comments

Comments
 (0)