json-edit-react 0.9.6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -26
- package/build/index.cjs.js +207 -51
- package/build/index.d.ts +44 -4
- package/build/index.esm.js +208 -49
- package/package.json +11 -11
package/build/index.cjs.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
4
|
var react = require('react');
|
|
7
5
|
var assign = require('object-property-assigner');
|
|
@@ -28,7 +26,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
28
26
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
29
27
|
PERFORMANCE OF THIS SOFTWARE.
|
|
30
28
|
***************************************************************************** */
|
|
31
|
-
/* global Reflect, Promise */
|
|
29
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
32
30
|
|
|
33
31
|
|
|
34
32
|
function __rest(s, e) {
|
|
@@ -51,7 +49,12 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
51
49
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
52
50
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
53
51
|
});
|
|
54
|
-
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
55
|
+
var e = new Error(message);
|
|
56
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
57
|
+
};
|
|
55
58
|
|
|
56
59
|
var e=[],t=[];function n(n,r){if(n&&"undefined"!=typeof document){var a,s=!0===r.prepend?"prepend":"append",d=!0===r.singleTag,i="string"==typeof r.container?document.querySelector(r.container):document.getElementsByTagName("head")[0];if(d){var u=e.indexOf(i);-1===u&&(u=e.push(i)-1,t[u]={}),a=t[u]&&t[u][s]?t[u][s]:t[u][s]=c();}else a=c();65279===n.charCodeAt(0)&&(n=n.substring(1)),a.styleSheet?a.styleSheet.cssText+=n:a.appendChild(document.createTextNode(n));}function c(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),r.attributes)for(var t=Object.keys(r.attributes),n=0;n<t.length;n++)e.setAttribute(t[n],r.attributes[t[n]]);var a="prepend"===s?"afterbegin":"beforeend";return i.insertAdjacentElement(a,e),e}}
|
|
57
60
|
|
|
@@ -314,7 +317,7 @@ const ThemeProvider = ({ children }) => {
|
|
|
314
317
|
const [styles, setStyles] = react.useState(emptyStyleObject);
|
|
315
318
|
const [icons, setIcons] = react.useState({});
|
|
316
319
|
const setTheme = (theme) => setStyles(compileStyles(theme));
|
|
317
|
-
return (jsxRuntime.jsx(ThemeProviderContext.Provider,
|
|
320
|
+
return (jsxRuntime.jsx(ThemeProviderContext.Provider, { value: { styles, setTheme, icons, setIcons }, children: children }));
|
|
318
321
|
};
|
|
319
322
|
const useTheme = () => react.useContext(ThemeProviderContext);
|
|
320
323
|
const compileStyles = (themeInput) => {
|
|
@@ -388,10 +391,10 @@ const defaultStyleProperties = {
|
|
|
388
391
|
const AutogrowTextArea = ({ className, name, value, setValue, handleKeyPress, }) => {
|
|
389
392
|
const { styles } = useTheme();
|
|
390
393
|
const dummyValue = value.slice(-1) === '\n' ? value + '.' : value;
|
|
391
|
-
return (jsxRuntime.jsxs("div",
|
|
394
|
+
return (jsxRuntime.jsxs("div", { style: { display: 'grid' }, children: [jsxRuntime.jsx("textarea", { style: Object.assign({ height: 'auto', gridArea: '1 / 1 / 2 / 2', overflowY: 'auto' }, styles.input), rows: 1, className: className, name: name, value: value, onChange: (e) => setValue(e.target.value), autoFocus: true, onFocus: (e) => {
|
|
392
395
|
if (value.length < 40)
|
|
393
396
|
e.target.select();
|
|
394
|
-
}, onKeyDown: handleKeyPress }), jsxRuntime.jsx("span",
|
|
397
|
+
}, onKeyDown: handleKeyPress }), jsxRuntime.jsx("span", { className: className, style: Object.assign({ visibility: 'hidden', height: 'auto', gridArea: '1 / 1 / 2 / 2', color: 'red', opacity: 0.9, whiteSpace: 'pre-wrap', overflow: 'clip', border: '1px solid transparent' }, styles.input), children: dummyValue })] }));
|
|
395
398
|
};
|
|
396
399
|
|
|
397
400
|
const INVALID_FUNCTION_STRING = '**INVALID_FUNCTION**';
|
|
@@ -405,10 +408,10 @@ const StringValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdi
|
|
|
405
408
|
handleCancel();
|
|
406
409
|
};
|
|
407
410
|
const breakString = (text) => text.split('\n').map((line, index, arr) => (jsxRuntime.jsxs("span", { children: [line, index < arr.length - 1 ? jsxRuntime.jsx("br", {}) : null] }, index)));
|
|
408
|
-
return isEditing ? (jsxRuntime.jsx(AutogrowTextArea, { className: "jer-input-text", name: path.join('.'), value: value, setValue: setValue, isEditing: isEditing, handleKeyPress: handleKeyPress })) : (jsxRuntime.jsxs("div",
|
|
411
|
+
return isEditing ? (jsxRuntime.jsx(AutogrowTextArea, { className: "jer-input-text", name: path.join('.'), value: value, setValue: setValue, isEditing: isEditing, handleKeyPress: handleKeyPress })) : (jsxRuntime.jsxs("div", { onDoubleClick: () => setIsEditing(true), onClick: (e) => {
|
|
409
412
|
if (e.getModifierState('Control') || e.getModifierState('Meta'))
|
|
410
413
|
setIsEditing(true);
|
|
411
|
-
}, className: "jer-value-string", style: styles.string
|
|
414
|
+
}, className: "jer-value-string", style: styles.string, children: ["\"", breakString(truncate(value, stringTruncate)), "\""] }));
|
|
412
415
|
};
|
|
413
416
|
const NumberValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdit, handleCancel, }) => {
|
|
414
417
|
const { styles } = useTheme();
|
|
@@ -421,18 +424,70 @@ const NumberValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdi
|
|
|
421
424
|
const validateNumber = (input) => {
|
|
422
425
|
return input.replace(/[^0-9.-]/g, '');
|
|
423
426
|
};
|
|
424
|
-
return isEditing ? (jsxRuntime.jsx("input", { className: "jer-input-number", type: "text", name: path.join('.'), value: value, onChange: (e) => setValue(validateNumber(e.target.value)), autoFocus: true, onFocus: (e) => e.target.select(), onKeyDown: handleKeyPress, style: { width: `${String(value).length / 1.5 + 2}em` } })) : (jsxRuntime.jsx("span",
|
|
427
|
+
return isEditing ? (jsxRuntime.jsx("input", { className: "jer-input-number", type: "text", name: path.join('.'), value: value, onChange: (e) => setValue(validateNumber(e.target.value)), autoFocus: true, onFocus: (e) => e.target.select(), onKeyDown: handleKeyPress, style: { width: `${String(value).length / 1.5 + 2}em` } })) : (jsxRuntime.jsx("span", { onDoubleClick: () => setIsEditing(true), className: "jer-value-number", style: styles.number, children: value }));
|
|
425
428
|
};
|
|
426
|
-
const BooleanValue = ({ value, setValue, isEditing, path, setIsEditing, }) => {
|
|
429
|
+
const BooleanValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdit, handleCancel, }) => {
|
|
427
430
|
const { styles } = useTheme();
|
|
428
|
-
|
|
431
|
+
react.useEffect(() => {
|
|
432
|
+
if (isEditing)
|
|
433
|
+
document.addEventListener('keydown', listenForSubmit);
|
|
434
|
+
return () => document.removeEventListener('keydown', listenForSubmit);
|
|
435
|
+
}, [isEditing]);
|
|
436
|
+
const listenForSubmit = (event) => {
|
|
437
|
+
if (event.key === 'Enter') {
|
|
438
|
+
handleEdit();
|
|
439
|
+
}
|
|
440
|
+
else if (event.key === 'Escape')
|
|
441
|
+
handleCancel();
|
|
442
|
+
};
|
|
443
|
+
return isEditing ? (jsxRuntime.jsx("input", { className: "jer-input-boolean", type: "checkbox", name: path.join('.'), checked: value, onChange: () => setValue(!value) })) : (jsxRuntime.jsx("span", { onDoubleClick: () => setIsEditing(true), className: "jer-value-boolean", style: styles.boolean, children: String(value) }));
|
|
429
444
|
};
|
|
430
|
-
const NullValue = ({ value, isEditing, setIsEditing }) => {
|
|
445
|
+
const NullValue = ({ value, isEditing, setIsEditing, handleEdit, handleCancel, }) => {
|
|
431
446
|
const { styles } = useTheme();
|
|
432
|
-
|
|
447
|
+
react.useEffect(() => {
|
|
448
|
+
if (isEditing)
|
|
449
|
+
document.addEventListener('keydown', listenForSubmit);
|
|
450
|
+
return () => document.removeEventListener('keydown', listenForSubmit);
|
|
451
|
+
}, [isEditing]);
|
|
452
|
+
const listenForSubmit = (event) => {
|
|
453
|
+
if (event.key === 'Enter') {
|
|
454
|
+
handleEdit();
|
|
455
|
+
}
|
|
456
|
+
else if (event.key === 'Escape')
|
|
457
|
+
handleCancel();
|
|
458
|
+
};
|
|
459
|
+
return isEditing ? (jsxRuntime.jsx("div", { className: "jer-input-null", children: "null" })) : (jsxRuntime.jsx("div", { onDoubleClick: () => setIsEditing(true), className: "jer-value-null", style: styles.null, children: String(value) }));
|
|
460
|
+
};
|
|
461
|
+
const ObjectValue = ({ value, translate, isEditing, handleEdit, handleCancel, }) => {
|
|
462
|
+
react.useEffect(() => {
|
|
463
|
+
if (isEditing)
|
|
464
|
+
document.addEventListener('keydown', listenForSubmit);
|
|
465
|
+
return () => document.removeEventListener('keydown', listenForSubmit);
|
|
466
|
+
}, []);
|
|
467
|
+
const listenForSubmit = (event) => {
|
|
468
|
+
if (event.key === 'Enter') {
|
|
469
|
+
handleEdit();
|
|
470
|
+
}
|
|
471
|
+
else if (event.key === 'Escape')
|
|
472
|
+
handleCancel();
|
|
473
|
+
};
|
|
474
|
+
return (jsxRuntime.jsx("span", { className: "jer-value-object", children: `{${translate('DEFAULT_NEW_KEY')}: "${value}" }` }));
|
|
475
|
+
};
|
|
476
|
+
const ArrayValue = ({ value, isEditing, handleEdit, handleCancel, }) => {
|
|
477
|
+
react.useEffect(() => {
|
|
478
|
+
if (isEditing)
|
|
479
|
+
document.addEventListener('keydown', listenForSubmit);
|
|
480
|
+
return () => document.removeEventListener('keydown', listenForSubmit);
|
|
481
|
+
}, []);
|
|
482
|
+
const listenForSubmit = (event) => {
|
|
483
|
+
if (event.key === 'Enter') {
|
|
484
|
+
handleEdit();
|
|
485
|
+
}
|
|
486
|
+
else if (event.key === 'Escape')
|
|
487
|
+
handleCancel();
|
|
488
|
+
};
|
|
489
|
+
return jsxRuntime.jsx("span", { className: "jer-value-array", children: `[${value === null ? '' : value}]` });
|
|
433
490
|
};
|
|
434
|
-
const ObjectValue = () => (jsxRuntime.jsx("span", Object.assign({ className: "jer-value-object" }, { children: '{ }' })));
|
|
435
|
-
const ArrayValue = ({ value }) => (jsxRuntime.jsx("span", Object.assign({ className: "jer-value-array" }, { children: `[${value === null ? '' : value}]` })));
|
|
436
491
|
const InvalidValue = ({ value }) => {
|
|
437
492
|
let message = 'Error!';
|
|
438
493
|
switch (typeof value) {
|
|
@@ -447,7 +502,7 @@ const InvalidValue = ({ value }) => {
|
|
|
447
502
|
message = 'Symbol';
|
|
448
503
|
break;
|
|
449
504
|
}
|
|
450
|
-
return jsxRuntime.jsx("span",
|
|
505
|
+
return jsxRuntime.jsx("span", { className: "jer-value-invalid", children: message });
|
|
451
506
|
};
|
|
452
507
|
|
|
453
508
|
const Icon = ({ name, rotate }) => {
|
|
@@ -511,22 +566,22 @@ const EditButtons = ({ startEdit, handleDelete, handleAdd, enableClipboard, type
|
|
|
511
566
|
if (typeof enableClipboard === 'function')
|
|
512
567
|
enableClipboard({ value, stringValue, path, key: name, type: copyType });
|
|
513
568
|
};
|
|
514
|
-
return (jsxRuntime.jsxs("div",
|
|
569
|
+
return (jsxRuntime.jsxs("div", { className: "jer-edit-buttons", style: isAdding ? { opacity: 1 } : undefined, children: [enableClipboard && (jsxRuntime.jsx("div", { onClick: handleCopy, className: "jer-copy-pulse", children: jsxRuntime.jsx(Icon, { name: "copy" }) })), startEdit && (jsxRuntime.jsx("div", { onClick: startEdit, children: jsxRuntime.jsx(Icon, { name: "edit" }) })), handleDelete && (jsxRuntime.jsx("div", { onClick: handleDelete, children: jsxRuntime.jsx(Icon, { name: "delete" }) })), handleAdd && (jsxRuntime.jsx("div", { onClick: () => {
|
|
515
570
|
if (type === 'object')
|
|
516
571
|
setIsAdding(true);
|
|
517
572
|
else
|
|
518
573
|
handleAdd('');
|
|
519
|
-
}
|
|
574
|
+
}, children: jsxRuntime.jsx(Icon, { name: "add" }) })), isAdding && handleAdd && type === 'object' && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("input", { className: "jer-input-new-key", type: "text", name: "new-object-key", value: newKey, onChange: (e) => setNewKey(e.target.value), autoFocus: true, onFocus: (e) => e.target.select(), onKeyDown: handleKeyPress, style: Object.assign({}, styles.input) }), jsxRuntime.jsx(InputButtons, { onOk: () => {
|
|
520
575
|
if (!!newKey) {
|
|
521
576
|
setIsAdding(false);
|
|
522
577
|
handleAdd(newKey);
|
|
523
578
|
}
|
|
524
579
|
}, onCancel: () => {
|
|
525
580
|
setIsAdding(false);
|
|
526
|
-
} })] }))] }))
|
|
581
|
+
} })] }))] }));
|
|
527
582
|
};
|
|
528
583
|
const InputButtons = ({ onOk, onCancel }) => {
|
|
529
|
-
return (jsxRuntime.jsxs("div",
|
|
584
|
+
return (jsxRuntime.jsxs("div", { className: "jer-confirm-buttons", children: [jsxRuntime.jsx("div", { onClick: onOk, children: jsxRuntime.jsx(Icon, { name: "ok" }) }), jsxRuntime.jsx("div", { onClick: onCancel, children: jsxRuntime.jsx(Icon, { name: "cancel" }) })] }));
|
|
530
585
|
};
|
|
531
586
|
const stringifyPath = (path) => path.reduce((str, part) => {
|
|
532
587
|
if (typeof part === 'number')
|
|
@@ -540,9 +595,27 @@ const ValueDataTypes = ['string', 'number', 'boolean', 'null'];
|
|
|
540
595
|
const CollectionDataTypes = ['object', 'array'];
|
|
541
596
|
const DataTypes = [...ValueDataTypes, ...CollectionDataTypes];
|
|
542
597
|
|
|
543
|
-
const
|
|
598
|
+
const getCustomNode = (customNodeDefinitions = [], filterProps) => {
|
|
599
|
+
const matchingDefinitions = customNodeDefinitions.filter(({ condition }) => condition(filterProps));
|
|
600
|
+
if (matchingDefinitions.length === 0)
|
|
601
|
+
return {};
|
|
602
|
+
const { element, props, hideKey = false } = matchingDefinitions[0];
|
|
603
|
+
return { CustomNode: element, customNodeProps: props, hideKey };
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
const CustomNodeWrapper = ({ name, hideKey, children, indent, }) => {
|
|
607
|
+
const { styles } = useTheme();
|
|
608
|
+
const indentStyle = indent ? { marginLeft: `${indent / 2}em` } : {};
|
|
609
|
+
return (jsxRuntime.jsx("div", { className: "jer-component jer-value-component", style: indentStyle, children: jsxRuntime.jsxs("div", { className: "jer-value-main-row", style: {
|
|
610
|
+
flexWrap: name.length > 10 ? 'wrap' : 'nowrap',
|
|
611
|
+
}, children: [!hideKey && (jsxRuntime.jsxs("label", { htmlFor: name, className: "jer-object-key", style: Object.assign(Object.assign({}, styles.property), { minWidth: `${Math.min(String(name).length + 1, 5)}ch`, flexShrink: name.length > 10 ? 1 : 0 }), children: [name, ":", ' '] })), jsxRuntime.jsx("div", { className: "jer-value-and-buttons", style: { paddingLeft: hideKey ? 0 : undefined }, children: children })] }) }));
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
const ValueNodeWrapper = (props) => {
|
|
615
|
+
const { data, parentData, name, path, onEdit, onDelete, enableClipboard, restrictEditFilter, restrictDeleteFilter, restrictTypeSelection, showLabel, stringTruncate, indent, translate, customNodeDefinitions, } = props;
|
|
544
616
|
const { styles } = useTheme();
|
|
545
617
|
const [isEditing, setIsEditing] = react.useState(false);
|
|
618
|
+
const [isEditingKey, setIsEditingKey] = react.useState(false);
|
|
546
619
|
const [value, setValue] = react.useState(typeof data === 'function' ? INVALID_FUNCTION_STRING : data);
|
|
547
620
|
const [error, setError] = react.useState(null);
|
|
548
621
|
const [dataType, setDataType] = react.useState(getDataType(data));
|
|
@@ -551,8 +624,14 @@ const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard,
|
|
|
551
624
|
setDataType(getDataType(data));
|
|
552
625
|
}, [data, error]);
|
|
553
626
|
const handleChangeDataType = (type) => {
|
|
554
|
-
|
|
555
|
-
|
|
627
|
+
const customNode = customNodeDefinitions.find((customNode) => customNode.name === type);
|
|
628
|
+
if (customNode) {
|
|
629
|
+
onEdit(customNode.defaultValue, path);
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
setValue(convertValue(value, type));
|
|
633
|
+
setDataType(type);
|
|
634
|
+
}
|
|
556
635
|
};
|
|
557
636
|
const logError = (errorString) => {
|
|
558
637
|
setError(errorString);
|
|
@@ -564,10 +643,10 @@ const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard,
|
|
|
564
643
|
let newValue;
|
|
565
644
|
switch (dataType) {
|
|
566
645
|
case 'object':
|
|
567
|
-
newValue = {};
|
|
646
|
+
newValue = { [translate('DEFAULT_NEW_KEY')]: value };
|
|
568
647
|
break;
|
|
569
648
|
case 'array':
|
|
570
|
-
newValue = value !== null ?
|
|
649
|
+
newValue = value !== null ? value : [];
|
|
571
650
|
break;
|
|
572
651
|
case 'number':
|
|
573
652
|
const n = Number(value);
|
|
@@ -584,9 +663,27 @@ const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard,
|
|
|
584
663
|
logError(error);
|
|
585
664
|
});
|
|
586
665
|
};
|
|
666
|
+
const handleEditKey = (newKey) => {
|
|
667
|
+
setIsEditingKey(false);
|
|
668
|
+
if (!parentData)
|
|
669
|
+
return;
|
|
670
|
+
const parentPath = path.slice(0, -1);
|
|
671
|
+
if (!newKey)
|
|
672
|
+
return;
|
|
673
|
+
const newData = Object.fromEntries(Object.entries(parentData).map(([key, val]) => (key === name ? [newKey, val] : [key, val])));
|
|
674
|
+
onEdit(newData, parentPath);
|
|
675
|
+
};
|
|
676
|
+
const handleKeyPress = (e) => {
|
|
677
|
+
if (e.key === 'Enter')
|
|
678
|
+
handleEditKey(e.target.value);
|
|
679
|
+
else if (e.key === 'Escape')
|
|
680
|
+
handleCancel();
|
|
681
|
+
};
|
|
587
682
|
const handleCancel = () => {
|
|
588
683
|
setIsEditing(false);
|
|
684
|
+
setIsEditingKey(false);
|
|
589
685
|
setValue(data);
|
|
686
|
+
setDataType(getDataType(data));
|
|
590
687
|
};
|
|
591
688
|
const handleDelete = () => {
|
|
592
689
|
onDelete(value, path).then((error) => {
|
|
@@ -597,8 +694,11 @@ const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard,
|
|
|
597
694
|
const filterProps = { key: name, path, level: path.length, value: data, size: null };
|
|
598
695
|
const canEdit = react.useMemo(() => !restrictEditFilter(filterProps), [filterProps]);
|
|
599
696
|
const canDelete = react.useMemo(() => !restrictDeleteFilter(filterProps), [filterProps]);
|
|
697
|
+
const isArray = typeof path.slice(-1)[0] === 'number';
|
|
698
|
+
const canEditKey = !isArray && canEdit && canDelete;
|
|
600
699
|
const inputProps = {
|
|
601
700
|
value,
|
|
701
|
+
parentData,
|
|
602
702
|
setValue,
|
|
603
703
|
isEditing,
|
|
604
704
|
setIsEditing: canEdit ? () => setIsEditing(true) : () => { },
|
|
@@ -606,11 +706,35 @@ const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard,
|
|
|
606
706
|
handleCancel,
|
|
607
707
|
path,
|
|
608
708
|
stringTruncate,
|
|
709
|
+
translate,
|
|
609
710
|
};
|
|
610
|
-
|
|
711
|
+
const { CustomNode, customNodeProps, hideKey } = getCustomNode(customNodeDefinitions, {
|
|
712
|
+
key: name,
|
|
713
|
+
path,
|
|
714
|
+
level: path.length,
|
|
715
|
+
value: data,
|
|
716
|
+
size: 0,
|
|
717
|
+
});
|
|
718
|
+
const allDataTypes = [
|
|
719
|
+
...customNodeDefinitions
|
|
720
|
+
.filter(({ showInTypesSelector = false }) => showInTypesSelector)
|
|
721
|
+
.map(({ name }) => name),
|
|
722
|
+
...DataTypes,
|
|
723
|
+
];
|
|
724
|
+
const allowedDataTypes = react.useMemo(() => {
|
|
725
|
+
if (typeof restrictTypeSelection === 'boolean')
|
|
726
|
+
return restrictTypeSelection ? [] : allDataTypes;
|
|
727
|
+
if (Array.isArray(restrictTypeSelection))
|
|
728
|
+
return restrictTypeSelection;
|
|
729
|
+
const result = restrictTypeSelection(filterProps);
|
|
730
|
+
if (typeof result === 'boolean')
|
|
731
|
+
return result ? [] : allDataTypes;
|
|
732
|
+
return result;
|
|
733
|
+
}, [filterProps, restrictTypeSelection]);
|
|
734
|
+
return CustomNode ? (jsxRuntime.jsx(CustomNodeWrapper, { name: name, hideKey: hideKey, indent: indent, children: jsxRuntime.jsx(CustomNode, Object.assign({ customProps: customNodeProps }, props)) })) : (jsxRuntime.jsx("div", { className: "jer-component jer-value-component", style: { marginLeft: `${indent / 2}em` }, children: jsxRuntime.jsxs("div", { className: "jer-value-main-row", style: {
|
|
611
735
|
flexWrap: name.length > 10 ? 'wrap' : 'nowrap',
|
|
612
|
-
}
|
|
613
|
-
!error && (jsxRuntime.jsx(EditButtons, { startEdit: canEdit ? () => setIsEditing(true) : undefined, handleDelete: canDelete ? handleDelete : undefined, data: data, enableClipboard: enableClipboard, name: name, path: path, translate: translate }))), isEditing && (jsxRuntime.jsxs("div",
|
|
736
|
+
}, children: [showLabel && !isEditingKey && (jsxRuntime.jsxs("label", { htmlFor: path.join('.'), className: "jer-object-key", style: Object.assign(Object.assign({}, styles.property), { minWidth: `${Math.min(String(name).length + 1, 5)}ch`, flexShrink: name.length > 10 ? 1 : 0 }), onDoubleClick: () => canEditKey && setIsEditingKey(true), children: [name, ":", ' '] })), showLabel && isEditingKey && (jsxRuntime.jsx("input", { className: "jer-object-key", type: "text", name: path.join('.'), defaultValue: name, autoFocus: true, onFocus: (e) => e.target.select(), onKeyDown: handleKeyPress, style: { width: `${String(name).length / 1.5 + 0.5}em` } })), jsxRuntime.jsxs("div", { className: "jer-value-and-buttons", children: [jsxRuntime.jsx("div", { className: "jer-input-component", children: getInputComponent(dataType, inputProps) }), isEditing ? (jsxRuntime.jsx(InputButtons, { onOk: handleEdit, onCancel: handleCancel })) : (dataType !== 'invalid' &&
|
|
737
|
+
!error && (jsxRuntime.jsx(EditButtons, { startEdit: canEdit ? () => setIsEditing(true) : undefined, handleDelete: canDelete ? handleDelete : undefined, data: data, enableClipboard: enableClipboard, name: name, path: path, translate: translate }))), isEditing && allowedDataTypes.length > 0 && (jsxRuntime.jsxs("div", { className: "jer-select", children: [jsxRuntime.jsx("select", { name: `${name}-type-select`, className: "jer-type-select", onChange: (e) => handleChangeDataType(e.target.value), value: dataType, children: allowedDataTypes.map((type) => (jsxRuntime.jsx("option", { value: type, children: type }, type))) }), jsxRuntime.jsx("span", { className: "focus" })] })), !isEditing && error && (jsxRuntime.jsx("span", { className: "jer-error-slug", style: styles.error, children: error }))] })] }) }));
|
|
614
738
|
};
|
|
615
739
|
const getDataType = (value) => {
|
|
616
740
|
if (typeof value === 'string')
|
|
@@ -635,7 +759,7 @@ const getInputComponent = (dataType, inputProps) => {
|
|
|
635
759
|
case 'null':
|
|
636
760
|
return jsxRuntime.jsx(NullValue, Object.assign({}, inputProps));
|
|
637
761
|
case 'object':
|
|
638
|
-
return jsxRuntime.jsx(ObjectValue, Object.assign({}, inputProps));
|
|
762
|
+
return jsxRuntime.jsx(ObjectValue, Object.assign({}, inputProps, { value: value }));
|
|
639
763
|
case 'array':
|
|
640
764
|
return jsxRuntime.jsx(ArrayValue, Object.assign({}, inputProps));
|
|
641
765
|
default:
|
|
@@ -654,7 +778,7 @@ const convertValue = (value, type) => {
|
|
|
654
778
|
case 'null':
|
|
655
779
|
return null;
|
|
656
780
|
case 'object':
|
|
657
|
-
return
|
|
781
|
+
return value;
|
|
658
782
|
case 'array':
|
|
659
783
|
return [value];
|
|
660
784
|
default:
|
|
@@ -664,10 +788,11 @@ const convertValue = (value, type) => {
|
|
|
664
788
|
|
|
665
789
|
const isCollection = (value) => value !== null && typeof value == 'object';
|
|
666
790
|
const CollectionNode = (_a) => {
|
|
667
|
-
var { data, path, name } = _a, props = __rest(_a, ["data", "path", "name"]);
|
|
791
|
+
var { data, path, name, parentData, showCollectionCount } = _a, props = __rest(_a, ["data", "path", "name", "parentData", "showCollectionCount"]);
|
|
668
792
|
const { styles } = useTheme();
|
|
669
|
-
const { onEdit, onAdd, onDelete, restrictEditFilter, restrictDeleteFilter, restrictAddFilter, collapseFilter, enableClipboard, indent, keySort, showArrayIndices, defaultValue, translate, } = props;
|
|
793
|
+
const { onEdit, onAdd, onDelete, restrictEditFilter, restrictDeleteFilter, restrictAddFilter, collapseFilter, enableClipboard, indent, keySort, showArrayIndices, defaultValue, translate, customNodeDefinitions, } = props;
|
|
670
794
|
const [isEditing, setIsEditing] = react.useState(false);
|
|
795
|
+
const [isEditingKey, setIsEditingKey] = react.useState(false);
|
|
671
796
|
const [stringifiedValue, setStringifiedValue] = react.useState(JSON.stringify(data, null, 2));
|
|
672
797
|
const [error, setError] = react.useState(null);
|
|
673
798
|
const collectionSize = Object.keys(data).length;
|
|
@@ -721,6 +846,22 @@ const CollectionNode = (_a) => {
|
|
|
721
846
|
return;
|
|
722
847
|
}
|
|
723
848
|
};
|
|
849
|
+
const handleEditKey = (newKey) => {
|
|
850
|
+
setIsEditingKey(false);
|
|
851
|
+
if (!parentData)
|
|
852
|
+
return;
|
|
853
|
+
const parentPath = path.slice(0, -1);
|
|
854
|
+
if (!newKey)
|
|
855
|
+
return;
|
|
856
|
+
const newData = Object.fromEntries(Object.entries(parentData).map(([key, val]) => (key === name ? [newKey, val] : [key, val])));
|
|
857
|
+
onEdit(newData, parentPath);
|
|
858
|
+
};
|
|
859
|
+
const handleKeyPressKeyEdit = (e) => {
|
|
860
|
+
if (e.key === 'Enter')
|
|
861
|
+
handleEditKey(e.target.value);
|
|
862
|
+
else if (e.key === 'Escape')
|
|
863
|
+
handleCancel();
|
|
864
|
+
};
|
|
724
865
|
const handleAdd = (key) => {
|
|
725
866
|
setCollapsed(false);
|
|
726
867
|
if (collectionType === 'array') {
|
|
@@ -755,7 +896,10 @@ const CollectionNode = (_a) => {
|
|
|
755
896
|
const canEdit = react.useMemo(() => !restrictEditFilter(filterProps), [filterProps]);
|
|
756
897
|
const canDelete = react.useMemo(() => !restrictDeleteFilter(filterProps), [filterProps]);
|
|
757
898
|
const canAdd = react.useMemo(() => !restrictAddFilter(filterProps), [filterProps]);
|
|
758
|
-
const
|
|
899
|
+
const canEditKey = parentData !== null && canEdit && canAdd && canDelete;
|
|
900
|
+
const isArray = typeof path.slice(-1)[0] === 'number';
|
|
901
|
+
const showLabel = showArrayIndices || !isArray;
|
|
902
|
+
const showCount = showCollectionCount === 'when-closed' ? collapsed : showCollectionCount;
|
|
759
903
|
const keyValueArray = Object.entries(data).map(([key, value]) => [
|
|
760
904
|
collectionType === 'array' ? Number(key) : key,
|
|
761
905
|
value,
|
|
@@ -763,20 +907,27 @@ const CollectionNode = (_a) => {
|
|
|
763
907
|
if (keySort && collectionType === 'object')
|
|
764
908
|
keyValueArray.sort(typeof keySort === 'function' ? (a, b) => keySort(a[0], b[0]) : undefined);
|
|
765
909
|
const numOfLines = JSON.stringify(data, null, 2).split('\n').length;
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
910
|
+
const { CustomNode, customNodeProps, hideKey } = getCustomNode(customNodeDefinitions, {
|
|
911
|
+
key: name,
|
|
912
|
+
path,
|
|
913
|
+
level: path.length,
|
|
914
|
+
value: data,
|
|
915
|
+
size: Object.keys(data).length,
|
|
916
|
+
});
|
|
917
|
+
return (jsxRuntime.jsx("div", { className: "jer-component fb-collection-component", style: { marginLeft: `${path.length === 0 ? 0 : indent / 2}em` }, children: CustomNode ? (jsxRuntime.jsx(CustomNodeWrapper, { name: name, hideKey: hideKey, children: jsxRuntime.jsx(CustomNode, Object.assign({ data: data, path: path, name: name, parentData: parentData, customProps: customNodeProps }, props)) })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "jer-collection-header-row", children: [jsxRuntime.jsx("div", { onClick: handleCollapse, children: jsxRuntime.jsx(Icon, { name: "chevron", rotate: collapsed }) }), jsxRuntime.jsxs("div", { className: "jer-collection-name", children: [!isEditingKey && (jsxRuntime.jsx("span", { style: styles.property, onDoubleClick: () => canEditKey && setIsEditingKey(true), children: showLabel && name !== '' && name !== undefined ? `${name}:` : null })), isEditingKey && (jsxRuntime.jsx("input", { className: "jer-collection-name", type: "text", name: path.join('.'), defaultValue: name, autoFocus: true, onFocus: (e) => e.target.select(), onKeyDown: handleKeyPressKeyEdit, style: { width: `${String(name).length / 1.5 + 0.5}em` } })), !isEditing && (jsxRuntime.jsx("span", { className: "jer-brackets", style: styles.bracket, children: brackets.open }))] }), !isEditing && showCollectionCount && (jsxRuntime.jsx("div", { className: `jer-collection-item-count${showCount ? ' jer-visible' : ' jer-hidden'}`, style: styles.itemCount, children: collectionSize === 1
|
|
918
|
+
? translate('ITEM_SINGLE', 1)
|
|
919
|
+
: translate('ITEMS_MULTIPLE', collectionSize) })), jsxRuntime.jsx("div", { className: `jer-brackets${collapsed ? ' jer-visible' : ' jer-hidden'}`, style: styles.bracket, children: brackets.close }), !isEditing && (jsxRuntime.jsx(EditButtons, { startEdit: canEdit
|
|
920
|
+
? () => {
|
|
921
|
+
setIsEditing(true);
|
|
922
|
+
setCollapsed(false);
|
|
923
|
+
}
|
|
924
|
+
: undefined, handleAdd: canAdd ? handleAdd : undefined, handleDelete: canDelete ? handleDelete : undefined, enableClipboard: enableClipboard, type: collectionType, data: data, name: name, path: path, translate: translate }))] }), jsxRuntime.jsxs("div", { className: 'jer-collection-inner', style: {
|
|
925
|
+
maxHeight: collapsed ? 0 : !isEditing ? `${numOfLines * 3}em` : undefined,
|
|
926
|
+
overflowY: collapsed || isAnimating ? 'hidden' : 'visible',
|
|
927
|
+
transition: `max-height ${transitionTime}`,
|
|
928
|
+
}, children: [isEditing ? (jsxRuntime.jsx("div", { className: "jer-collection-text-edit", children: jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(AutogrowTextArea, { className: "jer-collection-text-area", name: path.join('.'), value: stringifiedValue, setValue: setStringifiedValue, isEditing: isEditing, handleKeyPress: handleKeyPress }), jsxRuntime.jsx("div", { className: "jer-collection-input-button-row", children: jsxRuntime.jsx(InputButtons, { onOk: handleEdit, onCancel: handleCancel, isCollection: true }) })] }) })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: !hasBeenOpened.current
|
|
929
|
+
? null
|
|
930
|
+
: keyValueArray.map(([key, value]) => (jsxRuntime.jsx("div", { className: "jer-collection-element", children: isCollection(value) ? (jsxRuntime.jsx(CollectionNode, Object.assign({ data: value, parentData: data, path: [...path, key], name: key, showCollectionCount: showCollectionCount }, props), key)) : (jsxRuntime.jsx(ValueNodeWrapper, Object.assign({ data: value, parentData: data, path: [...path, key], name: key }, props, { showLabel: collectionType === 'object' ? true : showArrayIndices }), key)) }, key))) })), jsxRuntime.jsx("div", { className: isEditing ? 'jer-collection-error-row' : 'jer-collection-error-row-edit', children: error && (jsxRuntime.jsx("span", { className: "jer-error-slug", style: styles.error, children: error })) }), jsxRuntime.jsx("div", { className: "jer-brackets", style: styles.bracket, children: brackets.close })] })] })) }));
|
|
780
931
|
};
|
|
781
932
|
|
|
782
933
|
const localisedStrings = {
|
|
@@ -788,6 +939,8 @@ const localisedStrings = {
|
|
|
788
939
|
ERROR_UPDATE: 'Update unsuccessful',
|
|
789
940
|
ERROR_DELETE: 'Delete unsuccessful',
|
|
790
941
|
ERROR_ADD: 'Adding node unsuccessful',
|
|
942
|
+
DEFAULT_STRING: 'New data!',
|
|
943
|
+
DEFAULT_NEW_KEY: 'key',
|
|
791
944
|
};
|
|
792
945
|
const translate = (translations, key, count) => {
|
|
793
946
|
const string = key in translations ? translations[key] : localisedStrings[key];
|
|
@@ -795,7 +948,7 @@ const translate = (translations, key, count) => {
|
|
|
795
948
|
};
|
|
796
949
|
const getTranslateFunction = (translations) => (key, count) => translate(translations, key, count);
|
|
797
950
|
|
|
798
|
-
const Editor = ({ data: srcData, rootName = 'root', onUpdate, onEdit: srcEdit = onUpdate, onDelete: srcDelete = onUpdate, onAdd: srcAdd = onUpdate, enableClipboard = true, theme = 'default', icons, indent = 4, collapse = false, restrictEdit = false, restrictDelete = false, restrictAdd = false, keySort = false, showArrayIndices = true, defaultValue = null, minWidth = 250, maxWidth = 'min(600px, 90vw)', stringTruncate = 250, translations = {}, className, }) => {
|
|
951
|
+
const Editor = ({ data: srcData, rootName = 'root', onUpdate, onEdit: srcEdit = onUpdate, onDelete: srcDelete = onUpdate, onAdd: srcAdd = onUpdate, enableClipboard = true, theme = 'default', icons, indent = 4, collapse = false, showCollectionCount = true, restrictEdit = false, restrictDelete = false, restrictAdd = false, restrictTypeSelection = false, keySort = false, showArrayIndices = true, defaultValue = null, minWidth = 250, maxWidth = 'min(600px, 90vw)', stringTruncate = 250, translations = {}, className, customNodeDefinitions = [], }) => {
|
|
799
952
|
const [data, setData] = react.useState(srcData);
|
|
800
953
|
const { styles, setTheme, setIcons } = useTheme();
|
|
801
954
|
const collapseFilter = react.useCallback(getFilterFunction(collapse), [collapse]);
|
|
@@ -877,10 +1030,12 @@ const Editor = ({ data: srcData, rootName = 'root', onUpdate, onEdit: srcEdit =
|
|
|
877
1030
|
onEdit,
|
|
878
1031
|
onDelete,
|
|
879
1032
|
onAdd,
|
|
1033
|
+
showCollectionCount,
|
|
880
1034
|
collapseFilter,
|
|
881
1035
|
restrictEditFilter,
|
|
882
1036
|
restrictDeleteFilter,
|
|
883
1037
|
restrictAddFilter,
|
|
1038
|
+
restrictTypeSelection,
|
|
884
1039
|
enableClipboard,
|
|
885
1040
|
keySort,
|
|
886
1041
|
showArrayIndices,
|
|
@@ -888,10 +1043,12 @@ const Editor = ({ data: srcData, rootName = 'root', onUpdate, onEdit: srcEdit =
|
|
|
888
1043
|
defaultValue,
|
|
889
1044
|
stringTruncate,
|
|
890
1045
|
translate,
|
|
1046
|
+
customNodeDefinitions,
|
|
1047
|
+
parentData: null,
|
|
891
1048
|
};
|
|
892
1049
|
if (!styles)
|
|
893
1050
|
return null;
|
|
894
|
-
return (jsxRuntime.jsx("div",
|
|
1051
|
+
return (jsxRuntime.jsx("div", { className: 'jer-editor-container ' + className, style: Object.assign(Object.assign({}, styles.container), { minWidth, maxWidth }), children: isCollection(data) ? (jsxRuntime.jsx(CollectionNode, Object.assign({ data: data, path: [] }, otherProps))) : (jsxRuntime.jsx(ValueNodeWrapper, Object.assign({ data: data, path: [], showLabel: true }, otherProps))) }));
|
|
895
1052
|
};
|
|
896
1053
|
const JsonEditor = (props) => (jsxRuntime.jsx(ThemeProvider, { children: jsxRuntime.jsx(Editor, Object.assign({}, props)) }));
|
|
897
1054
|
const updateDataObject = (data, path, newValue, action) => {
|
|
@@ -921,5 +1078,4 @@ const getFilterFunction = (propValue) => {
|
|
|
921
1078
|
};
|
|
922
1079
|
|
|
923
1080
|
exports.JsonEditor = JsonEditor;
|
|
924
|
-
exports.default = JsonEditor;
|
|
925
1081
|
exports.themes = themes;
|
package/build/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ declare const localisedStrings: {
|
|
|
30
30
|
ERROR_UPDATE: string;
|
|
31
31
|
ERROR_DELETE: string;
|
|
32
32
|
ERROR_ADD: string;
|
|
33
|
+
DEFAULT_STRING: string;
|
|
34
|
+
DEFAULT_NEW_KEY: string;
|
|
33
35
|
};
|
|
34
36
|
type LocalisedStrings = typeof localisedStrings;
|
|
35
37
|
type TranslateFunction = (key: keyof LocalisedStrings, count?: number) => string;
|
|
@@ -47,10 +49,11 @@ interface JsonEditorProps {
|
|
|
47
49
|
className?: string;
|
|
48
50
|
indent?: number;
|
|
49
51
|
collapse?: boolean | number | FilterFunction;
|
|
52
|
+
showCollectionCount?: boolean | 'when-closed';
|
|
50
53
|
restrictEdit?: boolean | FilterFunction;
|
|
51
54
|
restrictDelete?: boolean | FilterFunction;
|
|
52
55
|
restrictAdd?: boolean | FilterFunction;
|
|
53
|
-
|
|
56
|
+
restrictTypeSelection?: boolean | DataType[] | TypeFilterFunction;
|
|
54
57
|
keySort?: boolean | CompareFunction;
|
|
55
58
|
showArrayIndices?: boolean;
|
|
56
59
|
defaultValue?: unknown;
|
|
@@ -58,8 +61,12 @@ interface JsonEditorProps {
|
|
|
58
61
|
maxWidth?: string | number;
|
|
59
62
|
stringTruncate?: number;
|
|
60
63
|
translations?: Partial<LocalisedStrings>;
|
|
64
|
+
customNodeDefinitions?: CustomNodeDefinition[];
|
|
61
65
|
}
|
|
66
|
+
declare const DataTypes: readonly ["string", "number", "boolean", "null", "object", "array"];
|
|
67
|
+
type DataType = (typeof DataTypes)[number] | 'invalid';
|
|
62
68
|
type CollectionKey = string | number;
|
|
69
|
+
type CollectionData = object | unknown[];
|
|
63
70
|
type ErrorString = string;
|
|
64
71
|
interface IconReplacements {
|
|
65
72
|
add?: JSX.Element;
|
|
@@ -78,13 +85,15 @@ type UpdateFunction = (props: {
|
|
|
78
85
|
name: CollectionKey;
|
|
79
86
|
path: CollectionKey[];
|
|
80
87
|
}) => void | ErrorString | false;
|
|
81
|
-
|
|
88
|
+
interface FilterProps {
|
|
82
89
|
key: CollectionKey;
|
|
83
90
|
path: CollectionKey[];
|
|
84
91
|
level: number;
|
|
85
92
|
value: unknown;
|
|
86
93
|
size: number | null;
|
|
87
|
-
}
|
|
94
|
+
}
|
|
95
|
+
type FilterFunction = (input: FilterProps) => boolean;
|
|
96
|
+
type TypeFilterFunction = (input: FilterProps) => boolean | DataType[];
|
|
88
97
|
type CopyType = 'path' | 'value';
|
|
89
98
|
type CopyFunction = (input: {
|
|
90
99
|
key: CollectionKey;
|
|
@@ -94,7 +103,38 @@ type CopyFunction = (input: {
|
|
|
94
103
|
type: CopyType;
|
|
95
104
|
}) => void;
|
|
96
105
|
type CompareFunction = (a: string, b: string) => number;
|
|
106
|
+
type OnChangeFunction = (value: unknown, path: (string | number)[]) => Promise<string | void>;
|
|
107
|
+
interface BaseNodeProps {
|
|
108
|
+
data: unknown;
|
|
109
|
+
parentData: CollectionData | null;
|
|
110
|
+
path: CollectionKey[];
|
|
111
|
+
name: CollectionKey;
|
|
112
|
+
onEdit: OnChangeFunction;
|
|
113
|
+
onDelete: OnChangeFunction;
|
|
114
|
+
enableClipboard: boolean | CopyFunction;
|
|
115
|
+
restrictEditFilter: FilterFunction;
|
|
116
|
+
restrictDeleteFilter: FilterFunction;
|
|
117
|
+
restrictAddFilter: FilterFunction;
|
|
118
|
+
restrictTypeSelection: boolean | DataType[] | TypeFilterFunction;
|
|
119
|
+
stringTruncate: number;
|
|
120
|
+
indent: number;
|
|
121
|
+
translate: TranslateFunction;
|
|
122
|
+
customNodeDefinitions: CustomNodeDefinition[];
|
|
123
|
+
}
|
|
124
|
+
interface CustomNodeProps extends BaseNodeProps {
|
|
125
|
+
customProps?: Record<string, unknown>;
|
|
126
|
+
parentData: CollectionData | null;
|
|
127
|
+
}
|
|
128
|
+
interface CustomNodeDefinition {
|
|
129
|
+
condition: FilterFunction;
|
|
130
|
+
element: React$1.FC<CustomNodeProps>;
|
|
131
|
+
name: string;
|
|
132
|
+
props?: Record<string, unknown>;
|
|
133
|
+
hideKey?: boolean;
|
|
134
|
+
defaultValue: unknown;
|
|
135
|
+
showInTypesSelector?: boolean;
|
|
136
|
+
}
|
|
97
137
|
|
|
98
138
|
declare const JsonEditor: React$1.FC<JsonEditorProps>;
|
|
99
139
|
|
|
100
|
-
export { CompareFunction, CopyFunction, FilterFunction, IconReplacements, JsonEditor, JsonEditorProps, LocalisedStrings, Theme, ThemeInput, ThemeName, TranslateFunction, UpdateFunction,
|
|
140
|
+
export { type CompareFunction, type CopyFunction, type FilterFunction, type IconReplacements, JsonEditor, type JsonEditorProps, type LocalisedStrings, type Theme, type ThemeInput, type ThemeName, type TranslateFunction, type UpdateFunction, themes };
|