From decde458b15072e51c4b9b808dd8f5eb07f51ab4 Mon Sep 17 00:00:00 2001 From: lucataglia Date: Fri, 19 May 2023 17:45:35 +0200 Subject: [PATCH] Integrate expand/collapse buttons into the react-json-tree --- packages/react-json-tree/package.json | 3 + .../react-json-tree/src/JSONNestedNode.tsx | 15 ++- .../react-json-tree/src/expandableButtons.tsx | 106 ++++++++++++++++++ packages/react-json-tree/src/index.tsx | 25 ++++- packages/react-json-tree/src/types.ts | 2 +- packages/react-json-tree/test/index.spec.tsx | 2 +- 6 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 packages/react-json-tree/src/expandableButtons.tsx diff --git a/packages/react-json-tree/package.json b/packages/react-json-tree/package.json index 5190e755..8bee4142 100644 --- a/packages/react-json-tree/package.json +++ b/packages/react-json-tree/package.json @@ -46,6 +46,9 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", + "@fortawesome/fontawesome-svg-core": "^6.4.0", + "@fortawesome/free-solid-svg-icons": "^6.4.0", + "@fortawesome/react-fontawesome": "^0.2.0", "@types/lodash": "^4.14.194", "react-base16-styling": "^0.9.1" }, diff --git a/packages/react-json-tree/src/JSONNestedNode.tsx b/packages/react-json-tree/src/JSONNestedNode.tsx index 265d0c47..6a505adf 100644 --- a/packages/react-json-tree/src/JSONNestedNode.tsx +++ b/packages/react-json-tree/src/JSONNestedNode.tsx @@ -119,13 +119,16 @@ export default function JSONNestedNode(props: Props) { isCircular ? false : shouldExpandNodeInitially(keyPath, data, level) ); - useEffect(() => { - if (shouldExpandNode === undefined) { - return; - } + const defaultExpanded = shouldExpandNodeInitially(keyPath, data, level); - setExpanded(shouldExpandNode); - }, [shouldExpandNode]); + useEffect(() => { + switch(shouldExpandNode){ + case 'expand': setExpanded(true); break; + case'collapse': setExpanded(false); break; + case'default': setExpanded(defaultExpanded); break; + default: + } + }, [defaultExpanded, shouldExpandNode]); const handleClick = useCallback(() => { if (expandable) setExpanded(!expanded); diff --git a/packages/react-json-tree/src/expandableButtons.tsx b/packages/react-json-tree/src/expandableButtons.tsx new file mode 100644 index 00000000..a4e4818d --- /dev/null +++ b/packages/react-json-tree/src/expandableButtons.tsx @@ -0,0 +1,106 @@ +import React, { ReactNode } from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowDown, faArrowRight, faUndo } from '@fortawesome/free-solid-svg-icons'; +import { Expandable } from '.'; + +interface Props { + expandable?: Expandable; + expandableDefaultValue?: 'expand' | 'collapse'; + shouldExpandNode?: 'expand' | 'collapse' | 'default'; + setShouldExpandNode: any; +} + +interface ExpandButtonProps { + expandableDefaultValue?: 'expand' | 'collapse'; + expandIcon?: ReactNode; + shouldExpandNode?: 'expand' | 'collapse' | 'default'; + setShouldExpandNode: any; +} + +interface CollapseButtonProps { + expandableDefaultValue?: 'expand' | 'collapse'; + collapseIcon?: ReactNode; + shouldExpandNode?: 'expand' | 'collapse' | 'default'; + setShouldExpandNode: any; +} + +interface DefaultButtonProps { + defaultIcon?: ReactNode; + setShouldExpandNode: any; +} + +function ExpandableButtons({ + expandable, + expandableDefaultValue, + setShouldExpandNode, + shouldExpandNode + }: Props){ + if(!expandable){ + return <> + } + + return ( +
+ + + + + +
+ ) +} + +function ExpandButton({ expandableDefaultValue, expandIcon, shouldExpandNode, setShouldExpandNode }: ExpandButtonProps) { + const onExpand = () => setShouldExpandNode('expand'); + + if (shouldExpandNode === 'collapse' || (shouldExpandNode === 'default' && expandableDefaultValue === 'collapse')) { + return ( +
+ {expandIcon || } +
+ ); + } + + return <>; + } + + function CollapseButton({ expandableDefaultValue, collapseIcon, shouldExpandNode, setShouldExpandNode }: CollapseButtonProps) { + const onCollapse = () => setShouldExpandNode('collapse'); + + if (shouldExpandNode === 'expand' ||(shouldExpandNode === 'default' && expandableDefaultValue === 'expand')) { + return ( +
+ {collapseIcon || } +
+ ); + } + + return <>; + } + + function DefaultButton({defaultIcon, setShouldExpandNode }:DefaultButtonProps) { + const onDefaultCollapse = () => setShouldExpandNode('default'); + + return ( +
+ {defaultIcon || } +
+ ); + + return <>; + } + + export default ExpandableButtons \ No newline at end of file diff --git a/packages/react-json-tree/src/index.tsx b/packages/react-json-tree/src/index.tsx index 48d1ceaa..f3df0b66 100644 --- a/packages/react-json-tree/src/index.tsx +++ b/packages/react-json-tree/src/index.tsx @@ -3,11 +3,12 @@ // Dave Vedder http://www.eskimospy.com/ // port by Daniele Zannotti http://www.github.com/dzannotti -import React, { useMemo } from 'react'; +import React, { ReactNode, useMemo, useState } from 'react'; import JSONNode from './JSONNode'; import createStylingFromTheme from './createStylingFromTheme'; import { invertTheme } from 'react-base16-styling'; import type { StylingValue, Theme } from 'react-base16-styling'; +import ExpandableButtons from './expandableButtons' import type { CommonExternalProps, GetItemString, @@ -20,6 +21,14 @@ interface Props extends Partial { data: unknown; theme?: Theme; invertTheme?: boolean; + expandable?: Expandable; +} + +interface Expandable { + defaultValue?: 'expand' | 'collapse'; + expandIcon?: ReactNode; + collapseIcon?: ReactNode; + defaultIcon?: ReactNode; } const identity = (value: any) => value; @@ -41,7 +50,7 @@ export function JSONTree({ labelRenderer = defaultLabelRenderer, valueRenderer = identity, shouldExpandNodeInitially = expandRootNode, - shouldExpandNode, + expandable, hideRoot = false, getItemString = defaultItemString, postprocessValue = identity, @@ -49,6 +58,9 @@ export function JSONTree({ collectionLimit = 50, sortObjectKeys = false, }: Props) { + const expandableDefaultValue = expandable?.defaultValue || 'expand' + const [shouldExpandNode, setShouldExpandNode] = useState(expandableDefaultValue); + const styling = useMemo( () => createStylingFromTheme(shouldInvertTheme ? invertTheme(theme) : theme), @@ -72,6 +84,13 @@ export function JSONTree({ collectionLimit={collectionLimit} sortObjectKeys={sortObjectKeys} /> + + ); } @@ -89,4 +108,4 @@ export type { Styling, CommonExternalProps, } from './types'; -export type { StylingValue }; +export type { Expandable, StylingValue }; diff --git a/packages/react-json-tree/src/types.ts b/packages/react-json-tree/src/types.ts index 2bb3981a..048a4086 100644 --- a/packages/react-json-tree/src/types.ts +++ b/packages/react-json-tree/src/types.ts @@ -47,7 +47,6 @@ export interface CommonExternalProps { labelRenderer: LabelRenderer; valueRenderer: ValueRenderer; shouldExpandNodeInitially: ShouldExpandNodeInitially; - shouldExpandNode: boolean | undefined; hideRoot: boolean; getItemString: GetItemString; postprocessValue: PostprocessValue; @@ -61,4 +60,5 @@ export interface CommonInternalProps extends CommonExternalProps { circularCache?: CircularCache; level?: number; isCircular?: boolean; + shouldExpandNode?: 'expand' | 'collapse' | 'default'; } diff --git a/packages/react-json-tree/test/index.spec.tsx b/packages/react-json-tree/test/index.spec.tsx index 99661291..7f409eda 100644 --- a/packages/react-json-tree/test/index.spec.tsx +++ b/packages/react-json-tree/test/index.spec.tsx @@ -17,6 +17,6 @@ describe('JSONTree', () => { const result = render(); expect(result.type).toBe('ul'); - expect(result.props.children.type.name).toBe(JSONNode.name); + expect(result.props.children[0].type.name).toBe(JSONNode.name); }); });