Skip to content

Commit a9628fc

Browse files
committed
feat: onEdit event function returns namespace`. (#6)
1 parent 9f655f0 commit a9628fc

File tree

8 files changed

+284
-128
lines changed

8 files changed

+284
-128
lines changed

core/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,11 @@ export default function Demo() {
530530
}}
531531
onEdit={(opts) => {
532532
console.log('opts:', opts)
533+
// opts.namespace: ['object', 'child', 'last']
534+
// opts.oldValue: null
535+
// opts.type: "value"
536+
// opts.value: "NULL3"
537+
return true;
533538
}}
534539
components={{
535540
objectKey: ObjectKey
@@ -756,6 +761,7 @@ export interface JsonViewEditorProps<T extends object> extends JsonViewProps<T>
756761
oldValue: unknown;
757762
keyName?: string | number;
758763
parentName?: string | number;
764+
namespace?: Array<string | number>;
759765
type?: 'value' | 'key';
760766
}) => boolean;
761767
/**

core/src/editor/index.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import type { CountInfoExtraProps } from './countInfoExtra';
88

99
export interface JsonViewEditorProps<T extends object> extends JsonViewProps<T> {
1010
/**
11-
* When a callback function is passed in, edit functionality is enabled. The callback is invoked before edits are completed.
11+
* When a callback function is passed in, edit functionality is enabled. The callback is invoked before edits are completed.
1212
* @returns {boolean} Returning false from onEdit will prevent the change from being made.
1313
*/
1414
onEdit?: (option: {
1515
value: unknown;
1616
oldValue: unknown;
1717
keyName?: string | number;
1818
parentName?: string | number;
19+
namespace?: Array<string | number>;
1920
type?: 'value' | 'key';
2021
}) => boolean;
2122
/**
@@ -24,8 +25,8 @@ export interface JsonViewEditorProps<T extends object> extends JsonViewProps<T>
2425
*/
2526
onAdd?: CountInfoExtraProps<T>['onAdd'];
2627
/**
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.
28+
* When a callback function is passed in, delete functionality is enabled. The callback is invoked before deletions are completed.
29+
* @returns Returning false from onDelete will prevent the change from being made.
2930
*/
3031
onDelete?: CountInfoExtraProps<T>['onDelete'];
3132
/** Whether enable edit feature. @default true */
@@ -37,14 +38,22 @@ const JsonViewEditor = forwardRef<HTMLDivElement, JsonViewEditorProps<object>>((
3738
const comps: JsonViewEditorProps<object>['components'] = {
3839
...components,
3940
countInfoExtra: (reprops) => <CountInfoExtra {...reprops} editable={editable} onAdd={onAdd} onDelete={onDelete} />,
40-
objectKey: (reprops) => <ObjectKey {...reprops} editableValue={editable} onEdit={onEdit} render={components?.objectKey} />,
41+
objectKey: (reprops) => (
42+
<ObjectKey {...reprops} editableValue={editable} onEdit={onEdit} render={components?.objectKey} />
43+
),
4144
value: (reprops) => {
42-
return <ReValue {...reprops} editableValue={editable} displayDataTypes={displayDataTypes} onDelete={onDelete} onEdit={onEdit} />
43-
}
44-
}
45-
return (
46-
<JsonView {...reset} displayDataTypes={false} components={comps} ref={ref} />
47-
);
45+
return (
46+
<ReValue
47+
{...reprops}
48+
editableValue={editable}
49+
displayDataTypes={displayDataTypes}
50+
onDelete={onDelete}
51+
onEdit={onEdit}
52+
/>
53+
);
54+
},
55+
};
56+
return <JsonView {...reset} displayDataTypes={false} components={comps} ref={ref} />;
4857
});
4958

5059
export default JsonViewEditor;

core/src/editor/objectKey.tsx

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,21 @@ export interface ObjectKeyProps<T extends object> extends SemicolonProps {
1010
editableValue?: boolean;
1111
}
1212

13-
export const ObjectKey: FC<ObjectKeyProps<object>>= (props) => {
14-
const { className, value, keyName, parentName, quotes, label, editableValue, onEdit, highlightUpdates = true, render, ...reset } = props;
13+
export const ObjectKey: FC<ObjectKeyProps<object>> = (props) => {
14+
const {
15+
className,
16+
value,
17+
keyName,
18+
parentName,
19+
quotes,
20+
label,
21+
namespace,
22+
editableValue,
23+
onEdit,
24+
highlightUpdates = true,
25+
render,
26+
...reset
27+
} = props;
1528
const [editable, setEditable] = useState(false);
1629
const [curentLabel, setCurentLabel] = useState(label);
1730
useEffect(() => setCurentLabel(label), [label]);
@@ -27,37 +40,50 @@ export const ObjectKey: FC<ObjectKeyProps<object>>= (props) => {
2740
$edit.current!.contentEditable = 'true';
2841
$edit.current?.focus();
2942
}
30-
}
43+
};
3144

32-
const blur = () => {
33-
setEditable(false);
45+
const blur = async () => {
3446
if ($edit.current) {
47+
if (onEdit) {
48+
const result = await onEdit({
49+
value: $edit.current.innerText,
50+
oldValue: curentLabel as string,
51+
namespace,
52+
keyName,
53+
parentName,
54+
type: 'key',
55+
});
56+
if (result) {
57+
setCurentLabel($edit.current.innerText);
58+
} else {
59+
$edit.current.innerText = curentLabel as string;
60+
}
61+
}
3562
$edit.current.contentEditable = 'false';
3663
$edit.current?.focus();
37-
setCurentLabel($edit.current.innerText);
38-
onEdit && onEdit({ value: $edit.current.innerText, oldValue: curentLabel as string, keyName, parentName, type: 'key' });
3964
}
40-
}
65+
setEditable(false);
66+
};
4167
const focus = () => {
4268
if ($edit.current) {
4369
$edit.current.contentEditable = 'true';
4470
$edit.current?.focus();
4571
}
46-
}
72+
};
4773
const keyDown = (evn: React.KeyboardEvent<HTMLSpanElement>) => {
4874
if (evn.key === 'Enter') {
4975
evn.stopPropagation();
5076
evn.preventDefault();
5177
$edit.current!.contentEditable = 'false';
5278
}
53-
}
79+
};
5480
useEffect(() => {
5581
if ($edit.current) {
56-
$edit.current.addEventListener("paste", (e) => {
82+
$edit.current.addEventListener('paste', (e) => {
5783
e.preventDefault();
5884
// @ts-ignore
59-
const text = e.clipboardData.getData("text/plain");
60-
document.execCommand("insertHTML", false, text);
85+
const text = e.clipboardData.getData('text/plain');
86+
document.execCommand('insertHTML', false, text);
6187
});
6288
}
6389
}, [$edit]);
@@ -75,15 +101,21 @@ export const ObjectKey: FC<ObjectKeyProps<object>>= (props) => {
75101
autoFocus: editable,
76102
suppressContentEditableWarning: true,
77103
children: editable ? curentLabel : content,
78-
}
104+
};
79105
if (render) {
80106
spanProps.value = value;
81107
spanProps.keyName = keyName;
82108
spanProps.parentName = parentName;
83-
return render({ className, ...reset, ...spanProps, parentName, label: curentLabel as string, children: editable ? curentLabel : content, ref: $edit });
109+
return render({
110+
className,
111+
...reset,
112+
...spanProps,
113+
parentName,
114+
label: curentLabel as string,
115+
children: editable ? curentLabel : content,
116+
ref: $edit,
117+
});
84118
}
85119

86120
return <Label className={className} {...reset} autoFocus={editable} {...spanProps} ref={$edit} />;
87-
}
88-
89-
121+
};

core/src/editor/value.tsx

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import { EditIcon } from './icon/edit';
66
import { DeleteIcon } from './icon/delete';
77
import type { JsonViewEditorProps } from './';
88

9-
const Quotes: FC<PropsWithChildren<React.HTMLAttributes<HTMLSpanElement> & { quotes?: JsonViewEditorProps<object>['quotes']; show?: boolean; }>> = ({ show, style, quotes }) => {
9+
const Quotes: FC<
10+
PropsWithChildren<
11+
React.HTMLAttributes<HTMLSpanElement> & { quotes?: JsonViewEditorProps<object>['quotes']; show?: boolean }
12+
>
13+
> = ({ show, style, quotes }) => {
1014
if (!quotes || !show) return;
1115
return <span style={style}>{quotes}</span>;
12-
}
16+
};
1317

1418
export interface ReValueProps<T extends object> extends React.HTMLAttributes<HTMLSpanElement> {
1519
onEdit?: JsonViewEditorProps<T>['onEdit'];
@@ -19,14 +23,31 @@ export interface ReValueProps<T extends object> extends React.HTMLAttributes<HTM
1923
value?: unknown;
2024
data?: T;
2125
visible?: boolean;
26+
namespace?: Array<string | number>;
2227
editableValue?: boolean;
2328
displayDataTypes?: boolean;
2429
setValue?: React.Dispatch<React.SetStateAction<T>>;
2530
onDelete?: JsonViewEditorProps<T>['onDelete'];
2631
}
2732

2833
export function ReValue<T extends object>(props: ReValueProps<T>) {
29-
const { type, value, setValue, data, keyName, visible, quotes, style, children, displayDataTypes, editableValue, onDelete, onEdit, ...reset } = props;
34+
const {
35+
type,
36+
value,
37+
setValue,
38+
data,
39+
keyName,
40+
visible,
41+
quotes,
42+
style,
43+
children,
44+
namespace,
45+
displayDataTypes,
46+
editableValue,
47+
onDelete,
48+
onEdit,
49+
...reset
50+
} = props;
3051
const [editable, setEditable] = useState(false);
3152
const $edit = useRef<HTMLSpanElement>(null);
3253
const [curentType, setCurentType] = useState(type);
@@ -40,7 +61,7 @@ export function ReValue<T extends object>(props: ReValueProps<T>) {
4061
$edit.current.setAttribute('contentEditable', 'true');
4162
$edit.current?.focus();
4263
}
43-
}
64+
};
4465
const keyDown = (evn: React.KeyboardEvent<HTMLSpanElement>) => {
4566
if (!editableValue) return;
4667
if (evn.key === 'Enter') {
@@ -51,7 +72,7 @@ export function ReValue<T extends object>(props: ReValueProps<T>) {
5172
$edit.current.setAttribute('contentEditable', 'false');
5273
}
5374
}
54-
}
75+
};
5576
const blur = async () => {
5677
if (!editableValue) return;
5778
setEditable(false);
@@ -70,29 +91,29 @@ export function ReValue<T extends object>(props: ReValueProps<T>) {
7091
if (Number.isNaN(text)) {
7192
typeStr = 'number';
7293
}
73-
if (typeof text === 'string' && /^(true|false)$/ig.test(text)) {
74-
text = /^(true)$/ig.test(text) ? true : false;
94+
if (typeof text === 'string' && /^(true|false)$/gi.test(text)) {
95+
text = /^(true)$/gi.test(text) ? true : false;
7596
typeStr = 'boolean';
76-
} else if (typeof text === 'string' && /^[\d]+n$/ig.test(text)) {
77-
text = BigInt(text.replace(/n$/ig, ''));
97+
} else if (typeof text === 'string' && /^[\d]+n$/gi.test(text)) {
98+
text = BigInt(text.replace(/n$/gi, ''));
7899
typeStr = 'bigint';
79-
} else if (typeof text === 'string' && /^(null)$/ig.test(text)) {
100+
} else if (typeof text === 'string' && /^(null)$/gi.test(text)) {
80101
text = null;
81102
typeStr = 'null';
82-
} else if (typeof text === 'string' && /^(undefined)$/ig.test(text)) {
103+
} else if (typeof text === 'string' && /^(undefined)$/gi.test(text)) {
83104
text = undefined;
84105
typeStr = 'undefined';
85106
} else if (typeof text === 'string') {
86107
try {
87-
if(text && text.length > 10 && !isNaN(Date.parse(text))){
108+
if (text && text.length > 10 && !isNaN(Date.parse(text))) {
88109
const dt = new Date(text);
89110
text = dt;
90111
typeStr = 'date';
91112
}
92113
} catch (error) {}
93114
}
94115
if (onEdit) {
95-
const result = await onEdit({ type: 'value', value: text, oldValue: curentChild });
116+
const result = await onEdit({ type: 'value', value: text, oldValue: curentChild, namespace });
96117
if (result) {
97118
setCurentType(typeStr);
98119
setCurentChild(text);
@@ -102,17 +123,22 @@ export function ReValue<T extends object>(props: ReValueProps<T>) {
102123
}
103124
}
104125
}
105-
}
106-
const defaultStyle = { minWidth: 34, minHeight: 18, paddingInline: 3, display: 'inline-block' } as React.CSSProperties;
126+
};
127+
const defaultStyle = {
128+
minWidth: 34,
129+
minHeight: 18,
130+
paddingInline: 3,
131+
display: 'inline-block',
132+
} as React.CSSProperties;
107133
const { type: typeStr, content: childStr } = getValueString(curentChild);
108134
const color = typeMap[typeStr]?.color || '';
109135
const spanProps: React.HTMLAttributes<HTMLSpanElement> = {
110136
...reset,
111137
onBlur: blur,
112138
onKeyDown: keyDown,
113139
spellCheck: false,
114-
style: editable ? {...style,...defaultStyle, color } : {...style, color},
115-
}
140+
style: editable ? { ...style, ...defaultStyle, color } : { ...style, color },
141+
};
116142
let typeView = <Type type={typeStr} />;
117143
if (typeStr === 'null' || typeStr === 'undefined') {
118144
typeView = <Fragment />;
@@ -123,16 +149,17 @@ export function ReValue<T extends object>(props: ReValueProps<T>) {
123149
delete (data as Record<string, any>)[keyName as string];
124150
setValue({ ...data } as T);
125151
}
126-
}
152+
};
127153
return (
128154
<Fragment>
129155
{displayDataTypes && typeView}
130156
<Quotes style={style} quotes={quotes} show={typeStr === 'string'} />
131-
<span {...spanProps} ref={$edit} data-value={childStr}>{typeof curentChild === 'string' ? curentChild : childStr}</span>
157+
<span {...spanProps} ref={$edit} data-value={childStr}>
158+
{typeof curentChild === 'string' ? curentChild : childStr}
159+
</span>
132160
<Quotes style={style} quotes={quotes} show={typeStr === 'string'} />
133161
{visible && editableValue && onEdit && <EditIcon onClick={click} />}
134162
{visible && editableValue && onDelete && <DeleteIcon onClick={deleteHandle} />}
135163
</Fragment>
136164
);
137-
138-
}
165+
}

0 commit comments

Comments
 (0)