Skip to content

Commit 396b8bc

Browse files
committed
feat(editor): add onDelete props.
1 parent 9d69d0a commit 396b8bc

File tree

13 files changed

+151
-24
lines changed

13 files changed

+151
-24
lines changed

core/README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const customTheme = {
126126
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
127127
'--w-rjv-edit-color': 'var(--w-rjv-color)',
128128
'--w-rjv-add-color': 'var(--w-rjv-color)',
129+
'--w-rjv-delete-color': '#dc3545',
129130
'--w-rjv-info-color': '#656565',
130131
'--w-rjv-update-color': '#ebcb8b',
131132
'--w-rjv-copied-color': '#9cdcfe',
@@ -192,6 +193,7 @@ const customTheme = {
192193
'--w-rjv-arrow-color': '#9cdcfe',
193194
'--w-rjv-edit-color': '#0184a6',
194195
'--w-rjv-add-color': '#0184a6',
196+
'--w-rjv-delete-color': '#dc3545',
195197
'--w-rjv-info-color': '#656565',
196198
'--w-rjv-update-color': '#ebcb8b',
197199
'--w-rjv-copied-color': '#0184a6',
@@ -331,7 +333,7 @@ const object = {
331333
integer: 42,
332334
}
333335

334-
function value({ type, children, value, keyName, visible, ...props }) {
336+
function value({ type, children, value, setValue, keyName, visible, ...props }) {
335337
if (type === 'string' && /\.(jpg)$/.test(value)) {
336338
return (
337339
<span {...props}>
@@ -384,7 +386,7 @@ export default function Demo() {
384386
import React from 'react';
385387
import JsonView from '@uiw/react-json-view';
386388

387-
function value({ type, children, visible, keyName, value, ...props }) {
389+
function value({ type, children, visible, keyName, value, setValue, ...props }) {
388390
if (value instanceof URL) {
389391
return (
390392
<span {...props}>
@@ -761,6 +763,11 @@ export interface JsonViewEditorProps<T extends object> extends JsonViewProps<T>
761763
* @returns {boolean} Returning false from onAdd will prevent the change from being made.
762764
*/
763765
onAdd?: CountInfoExtraProps<T>['onAdd'];
766+
/**
767+
* When a callback function is passed in, delete functionality is enabled. The callback is invoked before deletions are completed.
768+
* @returns Returning false from onDelete will prevent the change from being made.
769+
*/
770+
onDelete?: CountInfoExtraProps<T>['onDelete'];
764771
/** Whether enable edit feature. @default true */
765772
editable?: boolean;
766773
}

core/editor.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ declare module '@uiw/react-json-view/editor' {
2323
* @returns {boolean} Returning false from onAdd will prevent the change from being made.
2424
*/
2525
onAdd?: CountInfoExtraProps<T>['onAdd'];
26+
/**
27+
* When a callback function is passed in, delete functionality is enabled. The callback is invoked before deletions are completed.
28+
* @returns Returning false from onDelete will prevent the change from being made.
29+
*/
30+
onDelete?: CountInfoExtraProps<T>['onDelete'];
2631
/** Whether enable edit feature. @default true */
2732
editable?: boolean;
2833
}

core/src/editor/countInfoExtra.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ it('renders <CountInfoExtra /> onAdd test case', () => {
2222
expect(tree).toHaveProperty('children');
2323
expect(tree).toHaveProperty('type', 'svg');
2424
expect(tree).toHaveProperty('props.fill', 'var(--w-rjv-add-color, currentColor)')
25-
expect(tree).toHaveProperty('props.viewBox', '0 0 20 20')
25+
expect(tree).toHaveProperty('props.viewBox', '0 0 40 40')
2626
expect(tree).toHaveProperty('props.style', {
2727
verticalAlign: 'middle',
2828
display: 'inline-block',

core/src/editor/countInfoExtra.tsx

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,63 @@
1-
import { AddIcon } from './icon/add';
1+
import { Fragment } from 'react';
22
import type { CountInfoProps } from '../';
3+
import { AddIcon } from './icon/add';
4+
import { DeleteIcon } from './icon/delete';
35

46
export interface CountInfoExtraProps<T> extends Partial<CountInfoProps> {
57
editable: boolean;
68
showTools: boolean;
79
value: T;
10+
parentValue?: T;
11+
keyName?: string | number;
812
setValue?: React.Dispatch<React.SetStateAction<T>>
13+
setParentValue?: React.Dispatch<React.SetStateAction<T>>
914
/**
1015
* When a callback function is passed in, add functionality is enabled. The callback is invoked before additions are completed.
1116
* @returns {boolean} Returning false from onAdd will prevent the change from being made.
1217
*/
1318
onAdd?: (keyOrValue: string, newValue: T, value: T, isAdd: boolean) => boolean;
19+
/**
20+
* When a callback function is passed in, delete functionality is enabled. The callback is invoked before deletions are completed.
21+
* @returns Returning false from onDelete will prevent the change from being made.
22+
*/
23+
onDelete?: (keyName: string | number, value: T, parentValue: T) => boolean;
1424
}
1525

1626
export function CountInfoExtra<T extends object>(props: CountInfoExtraProps<T>) {
17-
const { visible, showTools, editable, value, setValue, onAdd } = props;
27+
const { visible, showTools, editable, keyName, value, parentValue, setValue, setParentValue, onAdd, onDelete } = props;
1828
if (!visible || !showTools) return null;
1929
const click = async (event: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
2030
event.stopPropagation();
2131
const keyOrValue = 'AddKeyOrValue';
2232
const isArray = Array.isArray(value);
2333
const isAdd = isArray ? true : !(keyOrValue in value);
2434
const result = isArray ? [...value, keyOrValue] : { ...value, [keyOrValue]: undefined };
25-
if (onAdd) {
35+
if (onAdd && setValue) {
2636
const maybeAdd = await onAdd(keyOrValue, result as T, props.value, isAdd);
2737
if (maybeAdd) {
2838
setValue!(result as T);
2939
}
3040
}
3141
}
32-
const svgProps: React.SVGProps<SVGSVGElement> = {
33-
onClick: click,
42+
const deleteHandle = async (event: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
43+
event.stopPropagation();
44+
if (onDelete && (keyName || typeof keyName === 'number') && parentValue) {
45+
const maybeDelete = await onDelete(keyName, value, parentValue as T);
46+
if (maybeDelete && setParentValue) {
47+
if (Array.isArray(parentValue)) {
48+
parentValue.splice(keyName as number, 1);
49+
setParentValue([...parentValue] as T);
50+
} else if (keyName in parentValue) {
51+
delete (parentValue as Record<string, any>)[keyName as string];
52+
setParentValue({...parentValue} as T);
53+
}
54+
}
55+
}
3456
}
35-
if (!editable || !onAdd) return;
3657
return (
37-
<AddIcon {...svgProps} />
58+
<Fragment>
59+
{editable && onAdd && <AddIcon onClick={click}/>}
60+
{editable && onDelete && parentValue && <DeleteIcon onClick={deleteHandle} />}
61+
</Fragment>
3862
);
3963
}

core/src/editor/icon/add.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ it('renders <AddIcon /> test case', () => {
1212
expect(tree).toHaveProperty('children');
1313
expect(tree).toHaveProperty('type', 'svg');
1414
expect(tree).toHaveProperty('props', {
15-
viewBox: '0 0 20 20',
15+
viewBox: '0 0 40 40',
1616
fill: 'var(--w-rjv-add-color, currentColor)',
1717
style: {
1818
verticalAlign: 'middle',

core/src/editor/icon/add.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export const AddIcon = (props: AddIconProps) => {
1212
width: '1em',
1313
}
1414
return (
15-
<svg viewBox="0 0 20 20" fill="var(--w-rjv-add-color, currentColor)" {...props} style={{ ...style, ...defaultStyle}}>
16-
<path d="M14.1970498,0 L5.81288651,0 C2.17107809,0 0,2.17 0,5.81 L0,14.18 C0,17.83 2.17107809,20 5.81288651,20 L14.1870449,20 C17.8288533,20 19.9999658,17.83 19.9999658,14.19 L19.9999658,5.81 C20.0099363,2.17 17.8388583,0 14.1970498,0 Z M16.0079491,10.75 L10.7553408,10.75 L10.7553408,16 C10.7553408,16.41 10.4151719,16.75 10.0049682,16.75 C9.59476448,16.75 9.25459556,16.41 9.25459556,16 L9.25459556,10.75 L4.00198727,10.75 C3.59178357,10.75 3.25161466,10.41 3.25161466,10 C3.25161466,9.59 3.59178357,9.25 4.00198727,9.25 L9.25459556,9.25 L9.25459556,4 C9.25459556,3.59 9.59476448,3.25 10.0049682,3.25 C10.4151719,3.25 10.7553408,3.59 10.7553408,4 L10.7553408,9.25 L16.0079491,9.25 C16.4181528,9.25 16.7583217,9.59 16.7583217,10 C16.7583217,10.41 16.4181528,10.75 16.0079491,10.75 Z" />
15+
<svg viewBox="0 0 40 40" fill="var(--w-rjv-add-color, currentColor)" {...props} style={{ ...style, ...defaultStyle}}>
16+
<path d="M28.3940997,0 L11.625773,0 C4.34215619,0 0,4.34 0,11.62 L0,28.36 C0,35.66 4.34215619,40 11.625773,40 L28.3740897,40 C35.6577066,40 39.9999316,35.66 39.9999316,28.38 L39.9999316,11.62 C40.0198727,4.34 35.6777165,0 28.3940997,0 Z M30.12022,21.8947368 L21.9056145,21.8947368 L21.9056145,30.1052632 C21.9056145,31.1410526 21.0462404,32 20.0099363,32 C18.9736323,32 18.1142582,31.1410526 18.1142582,30.1052632 L18.1142582,21.8947368 L9.89965272,21.8947368 C8.86334865,21.8947368 8.00397454,21.0357895 8.00397454,20 C8.00397454,18.9642105 8.86334865,18.1052632 9.89965272,18.1052632 L18.1142582,18.1052632 L18.1142582,9.89473684 C18.1142582,8.85894737 18.9736323,8 20.0099363,8 C21.0462404,8 21.9056145,8.85894737 21.9056145,9.89473684 L21.9056145,18.1052632 L30.12022,18.1052632 C31.156524,18.1052632 32.0158982,18.9642105 32.0158982,20 C32.0158982,21.0357895 31.156524,21.8947368 30.12022,21.8947368 Z" />
1717
</svg>
1818
);
1919
}

core/src/editor/icon/delete.test.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import renderer from 'react-test-renderer';
2+
import { DeleteIcon } from './delete';
3+
4+
5+
it('renders <DeleteIcon /> test case', () => {
6+
const component = renderer.create(
7+
<DeleteIcon />,
8+
);
9+
let tree = component.toJSON();
10+
expect(tree).toHaveProperty('type');
11+
expect(tree).toHaveProperty('props');
12+
expect(tree).toHaveProperty('children');
13+
expect(tree).toHaveProperty('type', 'svg');
14+
expect(tree).toHaveProperty('props', {
15+
viewBox: '0 0 40 40',
16+
fill: 'var(--w-rjv-delete-color, #dc3545)',
17+
style: {
18+
verticalAlign: 'middle',
19+
display: 'inline-block',
20+
cursor: 'pointer',
21+
marginLeft: 5,
22+
height: '1em',
23+
width: '1em',
24+
}
25+
});
26+
});

core/src/editor/icon/delete.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { CSSProperties } from 'react';
2+
3+
export interface DeleteIconProps extends React.SVGAttributes<SVGElement> {}
4+
export const DeleteIcon = (props: DeleteIconProps) => {
5+
const { style } = props
6+
const defaultStyle: CSSProperties = {
7+
verticalAlign: 'middle',
8+
display: 'inline-block',
9+
cursor: 'pointer',
10+
marginLeft: 5,
11+
height: '1em',
12+
width: '1em',
13+
}
14+
return (
15+
<svg viewBox="0 0 40 40" fill="var(--w-rjv-delete-color, #dc3545)" {...props} style={{ ...style, ...defaultStyle}}>
16+
<path d="M28.3940997,0 L11.625773,0 C4.34215619,0 0,4.34 0,11.62 L0,28.36 C0,35.66 4.34215619,40 11.625773,40 L28.3740897,40 C35.6577066,40 39.9999316,35.66 39.9999316,28.38 L39.9999316,11.62 C40.0198727,4.34 35.6777165,0 28.3940997,0 Z M30.3428039,27.0681654 C31.2346195,27.9595381 31.2346195,29.4349136 30.3428039,30.3262862 C29.88152,30.7873411 29.2972271,31.0025 28.7129341,31.0025 C28.1286412,31.0025 27.5443483,30.7873411 27.0830644,30.3262862 L20.0100446,23.2567787 L12.9370249,30.3262862 C12.475741,30.7873411 11.891448,31.0025 11.3071551,31.0025 C10.7228621,31.0025 10.1385692,30.7873411 9.67728531,30.3262862 C8.78464325,29.4234847 8.78464325,27.9709669 9.67728531,27.0681654 L16.7503051,19.9986579 L9.67728531,12.9291504 C8.78464325,12.0263488 8.78464325,10.573831 9.67728531,9.67102951 C10.5691008,8.77965683 12.0452093,8.77965683 12.9370249,9.67102951 L20.0100446,16.740537 L27.0830644,9.67102951 C27.9748799,8.77965683 29.4509884,8.77965683 30.3428039,9.67102951 C31.2346195,10.5624022 31.2346195,12.0377777 30.3428039,12.9291504 L23.2697842,19.9986579 L30.3428039,27.0681654 L30.3428039,27.0681654 Z" />
17+
</svg>
18+
);
19+
}

core/src/editor/index.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { forwardRef } from 'react'
1+
import { forwardRef } from 'react';
22
import JsonView from '../';
33
import type { JsonViewProps } from '../';
44
import { ObjectKey } from './objectKey';
@@ -23,18 +23,23 @@ export interface JsonViewEditorProps<T extends object> extends JsonViewProps<T>
2323
* @returns {boolean} Returning false from onAdd will prevent the change from being made.
2424
*/
2525
onAdd?: CountInfoExtraProps<T>['onAdd'];
26+
/**
27+
* When a callback function is passed in, delete functionality is enabled. The callback is invoked before deletions are completed.
28+
* @returns Returning false from onDelete will prevent the change from being made.
29+
*/
30+
onDelete?: CountInfoExtraProps<T>['onDelete'];
2631
/** Whether enable edit feature. @default true */
2732
editable?: boolean;
2833
}
2934

3035
const JsonViewEditor = forwardRef<HTMLDivElement, JsonViewEditorProps<object>>((props, ref) => {
31-
const { onEdit, components, editable = true, displayDataTypes = true, onAdd, ...reset } = props;
36+
const { onEdit, components, editable = true, displayDataTypes = true, onAdd, onDelete, ...reset } = props;
3237
const comps: JsonViewEditorProps<object>['components'] = {
3338
...components,
34-
countInfoExtra: (reprops) => <CountInfoExtra {...reprops} editable={editable} onAdd={onAdd} />,
39+
countInfoExtra: (reprops) => <CountInfoExtra {...reprops} editable={editable} onAdd={onAdd} onDelete={onDelete} />,
3540
objectKey: (reprops) => <ObjectKey {...reprops} editableValue={editable} onEdit={onEdit} render={components?.objectKey} />,
3641
value: (reprops) => {
37-
return <ReValue {...reprops} editableValue={editable} displayDataTypes={displayDataTypes} onEdit={onEdit} />
42+
return <ReValue {...reprops} editableValue={editable} displayDataTypes={displayDataTypes} onDelete={onDelete} onEdit={onEdit} />
3843
}
3944
}
4045
return (

core/src/editor/value.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Fragment, PropsWithChildren, useEffect, useRef, useState } from 'react'
33
import type { TypeProps } from '../value';
44
import { getValueString, isFloat, Type, typeMap } from '../value';
55
import { EditIcon } from './icon/edit';
6+
import { DeleteIcon } from './icon/delete';
67
import type { JsonViewEditorProps } from './';
78

89
const Quotes: FC<PropsWithChildren<React.HTMLAttributes<HTMLSpanElement> & { quotes?: JsonViewEditorProps<object>['quotes']; show?: boolean; }>> = ({ show, style, quotes }) => {
@@ -16,13 +17,16 @@ export interface ReValueProps<T extends object> extends React.HTMLAttributes<HTM
1617
keyName?: JsonViewEditorProps<T>['keyName'];
1718
type: TypeProps['type'];
1819
value?: unknown;
20+
data?: T;
1921
visible?: boolean;
2022
editableValue?: boolean;
2123
displayDataTypes?: boolean;
24+
setValue?: React.Dispatch<React.SetStateAction<T>>;
25+
onDelete?: JsonViewEditorProps<T>['onDelete'];
2226
}
2327

2428
export function ReValue<T extends object>(props: ReValueProps<T>) {
25-
const { type, value, keyName, visible, quotes, style, children, displayDataTypes, editableValue, onEdit, ...reset } = props;
29+
const { type, value, setValue, data, keyName, visible, quotes, style, children, displayDataTypes, editableValue, onDelete, onEdit, ...reset } = props;
2630
const [editable, setEditable] = useState(false);
2731
const $edit = useRef<HTMLSpanElement>(null);
2832
const [curentType, setCurentType] = useState(type);
@@ -111,13 +115,21 @@ export function ReValue<T extends object>(props: ReValueProps<T>) {
111115
if (typeStr === 'null' || typeStr === 'undefined') {
112116
typeView = <Fragment />;
113117
}
118+
const deleteHandle = async (evn: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
119+
evn.stopPropagation();
120+
if (data && keyName && keyName in data && setValue) {
121+
delete (data as Record<string, any>)[keyName as string];
122+
setValue({ ...data } as T);
123+
}
124+
}
114125
return (
115126
<Fragment>
116127
{displayDataTypes && typeView}
117128
<Quotes style={style} quotes={quotes} show={typeStr === 'string'} />
118129
<span {...spanProps} ref={$edit} data-value={childStr}>{typeof curentChild === 'string' ? curentChild : childStr}</span>
119130
<Quotes style={style} quotes={quotes} show={typeStr === 'string'} />
120131
{visible && editableValue && onEdit && <EditIcon onClick={click} />}
132+
{visible && editableValue && onDelete && <DeleteIcon onClick={deleteHandle} />}
121133
</Fragment>
122134
);
123135

0 commit comments

Comments
 (0)