json-edit-react 0.9.4 → 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.
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { createContext, useState, useContext, useEffect, useMemo, useCallback } from 'react';
2
+ import { createContext, useState, useContext, useEffect, useMemo, useRef, useCallback } from 'react';
3
3
  import assign from 'object-property-assigner';
4
4
  import extract from 'object-property-extractor';
5
5
  import clone from 'just-clone';
@@ -24,7 +24,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
24
24
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
25
25
  PERFORMANCE OF THIS SOFTWARE.
26
26
  ***************************************************************************** */
27
- /* global Reflect, Promise */
27
+ /* global Reflect, Promise, SuppressedError, Symbol */
28
28
 
29
29
 
30
30
  function __rest(s, e) {
@@ -47,11 +47,16 @@ function __awaiter(thisArg, _arguments, P, generator) {
47
47
  function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
48
48
  step((generator = generator.apply(thisArg, _arguments || [])).next());
49
49
  });
50
- }
50
+ }
51
+
52
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
53
+ var e = new Error(message);
54
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
55
+ };
51
56
 
52
57
  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}}
53
58
 
54
- var css = ":root {\n --jer-select-border: #b6b6b6;\n --jer-select-focus: #777;\n --jer-select-arrow: #777;\n --jer-form-border: 1px solid #ededf0;\n --jer-form-border-focus: 1px solid #e2e2e2;\n --jer-expand-transition-time: 0.2s;\n --jer-highlight-color: #b3d8ff;\n}\n\n.jer-visible {\n opacity: 1;\n transition: var(--jer-expand-transition-time);\n}\n\n.jer-hidden {\n opacity: 0;\n transition: var(--jer-expand-transition-time);\n}\n\n/* Select styled as per:\nhttps://moderncss.dev/custom-select-styles-with-pure-css/\n*/\n\n.jer-select select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: transparent;\n border: none;\n padding: 0 1em 0 0;\n margin: 0;\n /* width: 100%; */\n font-family: inherit;\n font-size: inherit;\n color: black;\n cursor: inherit;\n line-height: inherit;\n z-index: 1;\n outline: none;\n}\nselect::-ms-expand {\n display: none;\n}\n\n.jer-select {\n display: grid;\n grid-template-areas: 'select';\n align-items: center;\n position: relative;\n min-width: 12ch;\n max-width: 15ch;\n border: 1px solid var(--jer-select-border);\n border-radius: 0.25em;\n padding: 0.25em 0.5em;\n font-size: 1em;\n cursor: pointer;\n line-height: 1.1;\n background-color: #fff;\n background-image: linear-gradient(to top, #f9f9f9, #fff 33%);\n}\n.jer-select select,\n.jer-select::after {\n grid-area: select;\n}\n.jer-select:not(.jer-select--multiple)::after {\n content: '';\n justify-self: end;\n width: 0.8em;\n height: 0.5em;\n background-color: var(--jer-select-arrow);\n -webkit-clip-path: polygon(100% 0%, 0 0%, 50% 100%);\n clip-path: polygon(100% 0%, 0 0%, 50% 100%);\n}\n\nselect:focus + .focus {\n position: absolute;\n top: -1px;\n left: -1px;\n right: -1px;\n bottom: -1px;\n border: 1px solid var(--jer-select-focus);\n border-radius: inherit;\n}\n\n.jer-editor-container {\n font-size: 1.1em;\n /* font-size: 20px; */\n padding: 1em;\n margin-top: 1em;\n margin-bottom: 1em;\n border-radius: 0.5em;\n}\n\n.jer-editor-container textarea {\n /* width: 90%; */\n color: var(--jer-input-color);\n border: var(--jer-form-border);\n border-radius: 0.3em;\n outline: none;\n}\n\n.jer-editor-container textarea:focus {\n border: var(--jer-form-border-focus);\n}\n\n.jer-editor-container input {\n border: var(--jer-form-border);\n border-radius: 0.3em;\n outline: none;\n}\n\n.jer-editor-container input:focus {\n border: var(--jer-form-border-focus);\n}\n\n/* Input highlighted text */\n.jer-editor-container ::selection {\n background-color: var(--jer-highlight-color);\n}\n\n.jer-editor-container select {\n /* color: red; */\n}\n\n.jer-component {\n}\n\n.jer-collection-header-row,\n.jer-value-main-row {\n display: flex;\n min-height: 1.7em;\n gap: 0.3em;\n align-items: center;\n}\n\n.jer-collection-header-row {\n display: flex;\n flex-wrap: wrap;\n}\n\n.jer-brackets {\n}\n\n.jer-collection-item-count {\n}\n\n.jer-collection-inner {\n position: relative;\n}\n\n.jer-collection-text-edit {\n display: flex;\n flex-direction: column;\n gap: 0.3em;\n align-items: flex-start;\n}\n\n.jer-collection-text-area {\n resize: both;\n padding-top: 0.2em;\n padding-left: 0.5em;\n padding-right: 0.5em;\n padding-bottom: 0;\n overflow: hidden;\n max-height: 40em;\n}\n\n.jer-collection-input-button-row {\n display: flex;\n justify-content: flex-end;\n font-size: 150%;\n}\n\n.jer-collection-element {\n}\n\n.jer-collection-error-row {\n position: absolute;\n bottom: 0;\n}\n\n.jer-collection-error-row-edit {\n}\n\n.jer-error-slug {\n margin-left: 1em;\n}\n\n.jer-value-component {\n position: relative;\n}\n\n.jer-value-main-row {\n display: flex;\n gap: 0;\n}\n\n.jer-value-and-buttons {\n display: flex;\n justify-content: flex-start;\n align-items: center;\n padding-left: 0.5em;\n}\n\n.jer-value-error-row {\n position: absolute;\n}\n\n.jer-value-string {\n line-height: 1.3em;\n}\n\n.jer-input-text {\n resize: none;\n margin: 0;\n height: 1.4em;\n padding-left: 0.5em;\n padding-right: 0.5em;\n padding-bottom: 0;\n min-width: 6em;\n overflow: hidden;\n max-height: 30em;\n}\n\n.jer-value-number {\n}\n\n.jer-value-boolean {\n}\n\n.jer-input-boolean {\n transform: scale(1.5);\n margin-left: 0.3em;\n margin-right: 0.3em;\n}\n\n.jer-value-null {\n}\n\n.jer-value-object {\n}\n\n.jer-object-key {\n word-break: break-word;\n line-height: 1.1em;\n}\n\n.jer-value-array {\n}\n\n.jer-value-invalid {\n opacity: 0.5;\n font-style: italic;\n}\n\n/* .jer-input-text, */\n.jer-input-number {\n height: 1.6em;\n}\n\n.jer-input-number {\n min-width: 3em;\n}\n\n.jer-input-component {\n}\n\n.jer-edit-buttons,\n.jer-confirm-buttons {\n display: flex;\n align-items: center;\n cursor: pointer;\n}\n\n.jer-input-buttons {\n gap: 0.4em;\n}\n\n.jer-edit-buttons {\n gap: 0.4em;\n margin-left: 0.5em;\n opacity: 0;\n}\n\n.jer-confirm-buttons {\n gap: 0.2em;\n margin-left: 0.4em;\n}\n\n.jer-edit-buttons:hover {\n opacity: 1;\n position: relative;\n}\n\n.jer-collection-header-row:hover > .jer-edit-buttons {\n opacity: 1;\n}\n\n.jer-value-main-row:hover > .jer-edit-buttons {\n opacity: 1;\n}\n\n.jer-value-and-buttons:hover > .jer-edit-buttons {\n opacity: 1;\n}\n\n.jer-input-buttons {\n}\n\n.jer-icon-wrapper {\n height: 1.4em;\n opacity: 1;\n transition: 0.2s;\n}\n\n.jer-icon-wrapper:active {\n position: relative;\n top: 0.07em;\n}\n\n.jer-icon-wrapper:hover {\n opacity: 0.85;\n transition: 0.2s;\n transform: scale(1.2);\n}\n\n.jer-copy-pulse {\n position: relative;\n}\n\n.jer-copy-pulse:hover {\n}\n\n.jer-copy-pulse:after {\n content: '';\n display: block;\n position: absolute;\n border-radius: 50%;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n opacity: 0;\n transition: all 0.5s;\n box-shadow: 0 0 15px 5px var(--jer-icon-copy-color);\n}\n\n.jer-copy-pulse:active:after {\n box-shadow: 0 0 0 0 var(--jer-icon-copy-color);\n position: absolute;\n border-radius: 4em;\n left: 0;\n top: 0;\n opacity: 1;\n transition: 0s;\n}\n\n.jer-copy-pulse:active {\n top: 0.07em;\n}\n\n.jer-rotate-90 {\n transform: rotate(-90deg);\n transition: transform var(--jer-expand-transition-time);\n}\n\n.jer-icon {\n /* font-size: '2em'; */\n}\n\n.jer-icon:hover {\n opacity: 0.85;\n transition: 0.3s;\n transform: scale(1.2);\n}\n";
59
+ var css = ":root {\n --jer-select-border: #b6b6b6;\n --jer-select-focus: #777;\n --jer-select-arrow: #777;\n --jer-form-border: 1px solid #ededf0;\n --jer-form-border-focus: 1px solid #e2e2e2;\n --jer-expand-transition-time: 0.5s;\n --jer-highlight-color: #b3d8ff;\n}\n\n.jer-visible {\n opacity: 1;\n transition: var(--jer-expand-transition-time);\n}\n\n.jer-hidden {\n opacity: 0;\n transition: var(--jer-expand-transition-time);\n}\n\n/* Select styled as per:\nhttps://moderncss.dev/custom-select-styles-with-pure-css/\n*/\n\n.jer-select select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: transparent;\n border: none;\n padding: 0 1em 0 0;\n margin: 0;\n /* width: 100%; */\n font-family: inherit;\n font-size: inherit;\n color: black;\n cursor: inherit;\n line-height: inherit;\n z-index: 1;\n outline: none;\n}\nselect::-ms-expand {\n display: none;\n}\n\n.jer-select {\n display: grid;\n grid-template-areas: 'select';\n align-items: center;\n position: relative;\n min-width: 12ch;\n max-width: 15ch;\n border: 1px solid var(--jer-select-border);\n border-radius: 0.25em;\n padding: 0.25em 0.5em;\n font-size: 1em;\n cursor: pointer;\n line-height: 1.1;\n background-color: #fff;\n background-image: linear-gradient(to top, #f9f9f9, #fff 33%);\n}\n.jer-select select,\n.jer-select::after {\n grid-area: select;\n}\n.jer-select:not(.jer-select--multiple)::after {\n content: '';\n justify-self: end;\n width: 0.8em;\n height: 0.5em;\n background-color: var(--jer-select-arrow);\n -webkit-clip-path: polygon(100% 0%, 0 0%, 50% 100%);\n clip-path: polygon(100% 0%, 0 0%, 50% 100%);\n}\n\nselect:focus + .focus {\n position: absolute;\n top: -1px;\n left: -1px;\n right: -1px;\n bottom: -1px;\n border: 1px solid var(--jer-select-focus);\n border-radius: inherit;\n}\n\n.jer-editor-container {\n font-size: 1.1em;\n /* font-size: 20px; */\n padding: 1em;\n margin-top: 1em;\n margin-bottom: 1em;\n border-radius: 0.5em;\n}\n\n.jer-editor-container textarea {\n /* width: 90%; */\n color: var(--jer-input-color);\n border: var(--jer-form-border);\n border-radius: 0.3em;\n outline: none;\n}\n\n.jer-editor-container textarea:focus {\n border: var(--jer-form-border-focus);\n}\n\n.jer-editor-container input {\n border: var(--jer-form-border);\n border-radius: 0.3em;\n outline: none;\n}\n\n.jer-editor-container input:focus {\n border: var(--jer-form-border-focus);\n}\n\n/* Input highlighted text */\n.jer-editor-container ::selection {\n background-color: var(--jer-highlight-color);\n}\n\n.jer-editor-container select {\n /* color: red; */\n}\n\n.jer-component {\n}\n\n.jer-collection-header-row,\n.jer-value-main-row {\n display: flex;\n min-height: 1.7em;\n gap: 0.3em;\n align-items: center;\n}\n\n.jer-collection-header-row {\n display: flex;\n flex-wrap: wrap;\n}\n\n.jer-brackets {\n}\n\n.jer-collection-item-count {\n}\n\n.jer-collection-inner {\n position: relative;\n}\n\n.jer-collection-text-edit {\n display: flex;\n flex-direction: column;\n gap: 0.3em;\n align-items: flex-start;\n}\n\n.jer-collection-text-area {\n resize: both;\n padding-top: 0.2em;\n padding-left: 0.5em;\n padding-right: 0.5em;\n padding-bottom: 0;\n overflow: hidden;\n max-height: 40em;\n}\n\n.jer-collection-input-button-row {\n display: flex;\n justify-content: flex-end;\n font-size: 150%;\n}\n\n.jer-collection-element {\n}\n\n.jer-collection-error-row {\n position: absolute;\n bottom: 0;\n}\n\n.jer-collection-error-row-edit {\n}\n\n.jer-error-slug {\n margin-left: 1em;\n}\n\n.jer-value-component {\n position: relative;\n}\n\n.jer-value-main-row {\n display: flex;\n gap: 0;\n}\n\n.jer-value-and-buttons {\n display: flex;\n justify-content: flex-start;\n align-items: center;\n padding-left: 0.5em;\n}\n\n.jer-value-error-row {\n position: absolute;\n}\n\n.jer-value-string {\n line-height: 1.3em;\n}\n\n.jer-input-text {\n resize: none;\n margin: 0;\n height: 1.4em;\n padding-left: 0.5em;\n padding-right: 0.5em;\n padding-bottom: 0;\n min-width: 6em;\n overflow: hidden;\n max-height: 30em;\n}\n\n.jer-value-number {\n}\n\n.jer-value-boolean {\n}\n\n.jer-input-boolean {\n transform: scale(1.5);\n margin-left: 0.3em;\n margin-right: 0.3em;\n}\n\n.jer-value-null {\n}\n\n.jer-value-object {\n}\n\n.jer-object-key {\n word-break: break-word;\n line-height: 1.1em;\n}\n\n.jer-value-array {\n}\n\n.jer-value-invalid {\n opacity: 0.5;\n font-style: italic;\n}\n\n/* .jer-input-text, */\n.jer-input-number {\n height: 1.6em;\n}\n\n.jer-input-number {\n min-width: 3em;\n}\n\n.jer-input-component {\n}\n\n.jer-edit-buttons,\n.jer-confirm-buttons {\n display: flex;\n align-items: center;\n cursor: pointer;\n}\n\n.jer-input-buttons {\n gap: 0.4em;\n}\n\n.jer-edit-buttons {\n gap: 0.4em;\n margin-left: 0.5em;\n opacity: 0;\n}\n\n.jer-confirm-buttons {\n gap: 0.2em;\n margin-left: 0.4em;\n}\n\n.jer-edit-buttons:hover {\n opacity: 1;\n position: relative;\n}\n\n.jer-collection-header-row:hover > .jer-edit-buttons {\n opacity: 1;\n}\n\n.jer-value-main-row:hover > .jer-edit-buttons {\n opacity: 1;\n}\n\n.jer-value-and-buttons:hover > .jer-edit-buttons {\n opacity: 1;\n}\n\n.jer-input-buttons {\n}\n\n.jer-copy-pulse {\n position: relative;\n transition: transform var(--jer-expand-transition-time);\n}\n\n.jer-copy-pulse:hover {\n opacity: 0.85;\n transition: 0.3s;\n transform: scale(1.2);\n}\n\n.jer-copy-pulse:after {\n content: '';\n display: block;\n position: absolute;\n border-radius: 50%;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n opacity: 0;\n transition: all 0.5s;\n box-shadow: 0 0 15px 5px var(--jer-icon-copy-color);\n}\n\n.jer-copy-pulse:active:after {\n box-shadow: 0 0 0 0 var(--jer-icon-copy-color);\n position: absolute;\n border-radius: 4em;\n left: 0;\n top: 0;\n opacity: 1;\n transition: 0s;\n}\n\n.jer-copy-pulse:active {\n top: 0.07em;\n}\n\n.jer-rotate-90 {\n transform: rotate(-90deg);\n transition: transform var(--jer-expand-transition-time);\n}\n\n.jer-accordion-icon {\n /* font-size: '2em'; */\n transition: transform var(--jer-expand-transition-time);\n}\n\n.jer-icon:hover {\n opacity: 0.85;\n transition: 0.3s;\n transform: scale(1.2);\n}\n";
55
60
  n(css,{});
56
61
 
57
62
  const defaultTheme$1 = {
@@ -310,7 +315,7 @@ const ThemeProvider = ({ children }) => {
310
315
  const [styles, setStyles] = useState(emptyStyleObject);
311
316
  const [icons, setIcons] = useState({});
312
317
  const setTheme = (theme) => setStyles(compileStyles(theme));
313
- return (jsx(ThemeProviderContext.Provider, Object.assign({ value: { styles, setTheme, icons, setIcons } }, { children: children })));
318
+ return (jsx(ThemeProviderContext.Provider, { value: { styles, setTheme, icons, setIcons }, children: children }));
314
319
  };
315
320
  const useTheme = () => useContext(ThemeProviderContext);
316
321
  const compileStyles = (themeInput) => {
@@ -384,12 +389,13 @@ const defaultStyleProperties = {
384
389
  const AutogrowTextArea = ({ className, name, value, setValue, handleKeyPress, }) => {
385
390
  const { styles } = useTheme();
386
391
  const dummyValue = value.slice(-1) === '\n' ? value + '.' : value;
387
- return (jsxs("div", Object.assign({ style: { display: 'grid' } }, { children: [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
+ return (jsxs("div", { style: { display: 'grid' }, children: [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) => {
388
393
  if (value.length < 40)
389
394
  e.target.select();
390
- }, onKeyDown: handleKeyPress }), jsx("span", Object.assign({ 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
+ }, onKeyDown: handleKeyPress }), 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 })] }));
391
396
  };
392
397
 
398
+ const INVALID_FUNCTION_STRING = '**INVALID_FUNCTION**';
393
399
  const truncate = (string, length = 200) => string.length < length ? string : `${string.slice(0, length - 2).trim()}...`;
394
400
  const StringValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdit, handleCancel, stringTruncate, }) => {
395
401
  const { styles } = useTheme();
@@ -400,10 +406,10 @@ const StringValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdi
400
406
  handleCancel();
401
407
  };
402
408
  const breakString = (text) => text.split('\n').map((line, index, arr) => (jsxs("span", { children: [line, index < arr.length - 1 ? jsx("br", {}) : null] }, index)));
403
- return isEditing ? (jsx(AutogrowTextArea, { className: "jer-input-text", name: path.join('.'), value: value, setValue: setValue, isEditing: isEditing, handleKeyPress: handleKeyPress })) : (jsxs("div", Object.assign({ onDoubleClick: () => setIsEditing(true), onClick: (e) => {
409
+ return isEditing ? (jsx(AutogrowTextArea, { className: "jer-input-text", name: path.join('.'), value: value, setValue: setValue, isEditing: isEditing, handleKeyPress: handleKeyPress })) : (jsxs("div", { onDoubleClick: () => setIsEditing(true), onClick: (e) => {
404
410
  if (e.getModifierState('Control') || e.getModifierState('Meta'))
405
411
  setIsEditing(true);
406
- }, className: "jer-value-string", style: styles.string }, { children: ["\"", breakString(truncate(value, stringTruncate)), "\""] })));
412
+ }, className: "jer-value-string", style: styles.string, children: ["\"", breakString(truncate(value, stringTruncate)), "\""] }));
407
413
  };
408
414
  const NumberValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdit, handleCancel, }) => {
409
415
  const { styles } = useTheme();
@@ -416,24 +422,85 @@ const NumberValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdi
416
422
  const validateNumber = (input) => {
417
423
  return input.replace(/[^0-9.-]/g, '');
418
424
  };
419
- return isEditing ? (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` } })) : (jsx("span", Object.assign({ onDoubleClick: () => setIsEditing(true), className: "jer-value-number", style: styles.number }, { children: value })));
425
+ return isEditing ? (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` } })) : (jsx("span", { onDoubleClick: () => setIsEditing(true), className: "jer-value-number", style: styles.number, children: value }));
420
426
  };
421
- const BooleanValue = ({ value, setValue, isEditing, path, setIsEditing, }) => {
427
+ const BooleanValue = ({ value, setValue, isEditing, path, setIsEditing, handleEdit, handleCancel, }) => {
422
428
  const { styles } = useTheme();
423
- return isEditing ? (jsx("input", { className: "jer-input-boolean", type: "checkbox", name: path.join('.'), checked: value, onChange: () => setValue(!value) })) : (jsx("span", Object.assign({ onDoubleClick: () => setIsEditing(true), className: "jer-value-boolean", style: styles.boolean }, { children: String(value) })));
429
+ useEffect(() => {
430
+ if (isEditing)
431
+ document.addEventListener('keydown', listenForSubmit);
432
+ return () => document.removeEventListener('keydown', listenForSubmit);
433
+ }, [isEditing]);
434
+ const listenForSubmit = (event) => {
435
+ if (event.key === 'Enter') {
436
+ handleEdit();
437
+ }
438
+ else if (event.key === 'Escape')
439
+ handleCancel();
440
+ };
441
+ return isEditing ? (jsx("input", { className: "jer-input-boolean", type: "checkbox", name: path.join('.'), checked: value, onChange: () => setValue(!value) })) : (jsx("span", { onDoubleClick: () => setIsEditing(true), className: "jer-value-boolean", style: styles.boolean, children: String(value) }));
424
442
  };
425
- const NullValue = ({ value, isEditing, setIsEditing }) => {
443
+ const NullValue = ({ value, isEditing, setIsEditing, handleEdit, handleCancel, }) => {
426
444
  const { styles } = useTheme();
427
- return isEditing ? (jsx("div", Object.assign({ className: "jer-input-null" }, { children: "null" }))) : (jsx("div", Object.assign({ onDoubleClick: () => setIsEditing(true), className: "jer-value-null", style: styles.null }, { children: String(value) })));
445
+ useEffect(() => {
446
+ if (isEditing)
447
+ document.addEventListener('keydown', listenForSubmit);
448
+ return () => document.removeEventListener('keydown', listenForSubmit);
449
+ }, [isEditing]);
450
+ const listenForSubmit = (event) => {
451
+ if (event.key === 'Enter') {
452
+ handleEdit();
453
+ }
454
+ else if (event.key === 'Escape')
455
+ handleCancel();
456
+ };
457
+ return isEditing ? (jsx("div", { className: "jer-input-null", children: "null" })) : (jsx("div", { onDoubleClick: () => setIsEditing(true), className: "jer-value-null", style: styles.null, children: String(value) }));
428
458
  };
429
- const ObjectValue = () => {
430
- return jsx("span", Object.assign({ className: "jer-value-object" }, { children: '{ }' }));
459
+ const ObjectValue = ({ value, translate, isEditing, handleEdit, handleCancel, }) => {
460
+ useEffect(() => {
461
+ if (isEditing)
462
+ document.addEventListener('keydown', listenForSubmit);
463
+ return () => document.removeEventListener('keydown', listenForSubmit);
464
+ }, []);
465
+ const listenForSubmit = (event) => {
466
+ if (event.key === 'Enter') {
467
+ handleEdit();
468
+ }
469
+ else if (event.key === 'Escape')
470
+ handleCancel();
471
+ };
472
+ return (jsx("span", { className: "jer-value-object", children: `{${translate('DEFAULT_NEW_KEY')}: "${value}" }` }));
431
473
  };
432
- const ArrayValue = ({ value }) => {
433
- return jsx("span", Object.assign({ className: "jer-value-array" }, { children: `[${value === null ? '' : value}]` }));
474
+ const ArrayValue = ({ value, isEditing, handleEdit, handleCancel, }) => {
475
+ useEffect(() => {
476
+ if (isEditing)
477
+ document.addEventListener('keydown', listenForSubmit);
478
+ return () => document.removeEventListener('keydown', listenForSubmit);
479
+ }, []);
480
+ const listenForSubmit = (event) => {
481
+ if (event.key === 'Enter') {
482
+ handleEdit();
483
+ }
484
+ else if (event.key === 'Escape')
485
+ handleCancel();
486
+ };
487
+ return jsx("span", { className: "jer-value-array", children: `[${value === null ? '' : value}]` });
434
488
  };
435
- const InvalidValue = () => {
436
- return jsx("span", Object.assign({ className: "jer-value-invalid" }, { children: "Invalid value" }));
489
+ const InvalidValue = ({ value }) => {
490
+ let message = 'Error!';
491
+ switch (typeof value) {
492
+ case 'string':
493
+ if (value === INVALID_FUNCTION_STRING)
494
+ message = 'Function';
495
+ break;
496
+ case 'undefined':
497
+ message = 'Undefined';
498
+ break;
499
+ case 'symbol':
500
+ message = 'Symbol';
501
+ break;
502
+ }
503
+ return jsx("span", { className: "jer-value-invalid", children: message });
437
504
  };
438
505
 
439
506
  const Icon = ({ name, rotate }) => {
@@ -454,7 +521,7 @@ const Icon = ({ name, rotate }) => {
454
521
  case 'cancel':
455
522
  return ((_f = icons === null || icons === void 0 ? void 0 : icons.cancel) !== null && _f !== void 0 ? _f : (jsx(TiCancel, Object.assign({}, commonProps, { style: Object.assign({ fontSize: '130%' }, styles.iconCancel) }))));
456
523
  case 'chevron':
457
- return ((_g = icons === null || icons === void 0 ? void 0 : icons.chevron) !== null && _g !== void 0 ? _g : (jsx(FaChevronDown, { className: `${rotate ? ' jer-rotate-90' : ''}`, style: styles.iconCollection })));
524
+ return ((_g = icons === null || icons === void 0 ? void 0 : icons.chevron) !== null && _g !== void 0 ? _g : (jsx(FaChevronDown, { className: `jer-accordion-icon${rotate ? ' jer-rotate-90' : ''}`, style: styles.iconCollection })));
458
525
  default:
459
526
  return jsx(Fragment, {});
460
527
  }
@@ -478,38 +545,41 @@ const EditButtons = ({ startEdit, handleDelete, handleAdd, enableClipboard, type
478
545
  setIsAdding(false);
479
546
  };
480
547
  const handleCopy = (e) => {
481
- let type = 'value';
482
- let value = '';
548
+ let copyType = 'value';
549
+ let value;
550
+ let stringValue = '';
483
551
  if (enableClipboard) {
484
552
  switch (e.ctrlKey || e.metaKey) {
485
553
  case true:
486
554
  value = stringifyPath(path);
487
- type = 'path';
555
+ stringValue = value;
556
+ copyType = 'path';
488
557
  break;
489
558
  default:
490
- value = JSON.stringify(data, null, 2);
559
+ value = data;
560
+ stringValue = type ? JSON.stringify(data, null, 2) : String(value);
491
561
  }
492
- navigator.clipboard.writeText(value);
562
+ navigator.clipboard.writeText(stringValue);
493
563
  }
494
564
  if (typeof enableClipboard === 'function')
495
- enableClipboard({ value, path, key: name, type });
565
+ enableClipboard({ value, stringValue, path, key: name, type: copyType });
496
566
  };
497
- return (jsxs("div", Object.assign({ className: "jer-edit-buttons", style: isAdding ? { opacity: 1 } : undefined }, { children: [enableClipboard && (jsx("div", Object.assign({ onClick: handleCopy, className: "jer-copy-pulse" }, { children: jsx(Icon, { name: "copy" }) }))), startEdit && (jsx("div", Object.assign({ onClick: startEdit }, { children: jsx(Icon, { name: "edit" }) }))), handleDelete && (jsx("div", Object.assign({ onClick: handleDelete }, { children: jsx(Icon, { name: "delete" }) }))), handleAdd && (jsx("div", Object.assign({ onClick: () => {
567
+ return (jsxs("div", { className: "jer-edit-buttons", style: isAdding ? { opacity: 1 } : undefined, children: [enableClipboard && (jsx("div", { onClick: handleCopy, className: "jer-copy-pulse", children: jsx(Icon, { name: "copy" }) })), startEdit && (jsx("div", { onClick: startEdit, children: jsx(Icon, { name: "edit" }) })), handleDelete && (jsx("div", { onClick: handleDelete, children: jsx(Icon, { name: "delete" }) })), handleAdd && (jsx("div", { onClick: () => {
498
568
  if (type === 'object')
499
569
  setIsAdding(true);
500
570
  else
501
571
  handleAdd('');
502
- } }, { children: jsx(Icon, { name: "add" }) }))), isAdding && handleAdd && type === 'object' && (jsxs(Fragment, { children: [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) }), jsx(InputButtons, { onOk: () => {
572
+ }, children: jsx(Icon, { name: "add" }) })), isAdding && handleAdd && type === 'object' && (jsxs(Fragment, { children: [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) }), jsx(InputButtons, { onOk: () => {
503
573
  if (!!newKey) {
504
574
  setIsAdding(false);
505
575
  handleAdd(newKey);
506
576
  }
507
577
  }, onCancel: () => {
508
578
  setIsAdding(false);
509
- } })] }))] })));
579
+ } })] }))] }));
510
580
  };
511
581
  const InputButtons = ({ onOk, onCancel }) => {
512
- return (jsxs("div", Object.assign({ className: "jer-confirm-buttons" }, { children: [jsx("div", Object.assign({ onClick: onOk }, { children: jsx(Icon, { name: "ok" }) })), jsx("div", Object.assign({ onClick: onCancel }, { children: jsx(Icon, { name: "cancel" }) }))] })));
582
+ return (jsxs("div", { className: "jer-confirm-buttons", children: [jsx("div", { onClick: onOk, children: jsx(Icon, { name: "ok" }) }), jsx("div", { onClick: onCancel, children: jsx(Icon, { name: "cancel" }) })] }));
513
583
  };
514
584
  const stringifyPath = (path) => path.reduce((str, part) => {
515
585
  if (typeof part === 'number')
@@ -523,19 +593,43 @@ const ValueDataTypes = ['string', 'number', 'boolean', 'null'];
523
593
  const CollectionDataTypes = ['object', 'array'];
524
594
  const DataTypes = [...ValueDataTypes, ...CollectionDataTypes];
525
595
 
526
- const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard, restrictEditFilter, restrictDeleteFilter, showArrayIndices, stringTruncate, indent, translate, }) => {
596
+ const getCustomNode = (customNodeDefinitions = [], filterProps) => {
597
+ const matchingDefinitions = customNodeDefinitions.filter(({ condition }) => condition(filterProps));
598
+ if (matchingDefinitions.length === 0)
599
+ return {};
600
+ const { element, props, hideKey = false } = matchingDefinitions[0];
601
+ return { CustomNode: element, customNodeProps: props, hideKey };
602
+ };
603
+
604
+ const CustomNodeWrapper = ({ name, hideKey, children, indent, }) => {
605
+ const { styles } = useTheme();
606
+ const indentStyle = indent ? { marginLeft: `${indent / 2}em` } : {};
607
+ return (jsx("div", { className: "jer-component jer-value-component", style: indentStyle, children: jsxs("div", { className: "jer-value-main-row", style: {
608
+ flexWrap: name.length > 10 ? 'wrap' : 'nowrap',
609
+ }, children: [!hideKey && (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, ":", ' '] })), jsx("div", { className: "jer-value-and-buttons", style: { paddingLeft: hideKey ? 0 : undefined }, children: children })] }) }));
610
+ };
611
+
612
+ const ValueNodeWrapper = (props) => {
613
+ const { data, parentData, name, path, onEdit, onDelete, enableClipboard, restrictEditFilter, restrictDeleteFilter, restrictTypeSelection, showLabel, stringTruncate, indent, translate, customNodeDefinitions, } = props;
527
614
  const { styles } = useTheme();
528
615
  const [isEditing, setIsEditing] = useState(false);
529
- const [value, setValue] = useState(typeof data === 'function' ? 'INVALID_FUNCTION' : data);
616
+ const [isEditingKey, setIsEditingKey] = useState(false);
617
+ const [value, setValue] = useState(typeof data === 'function' ? INVALID_FUNCTION_STRING : data);
530
618
  const [error, setError] = useState(null);
531
619
  const [dataType, setDataType] = useState(getDataType(data));
532
620
  useEffect(() => {
533
- setValue(typeof data === 'function' ? 'INVALID_FUNCTION' : data);
621
+ setValue(typeof data === 'function' ? INVALID_FUNCTION_STRING : data);
534
622
  setDataType(getDataType(data));
535
623
  }, [data, error]);
536
624
  const handleChangeDataType = (type) => {
537
- setValue(convertValue(value, type));
538
- setDataType(type);
625
+ const customNode = customNodeDefinitions.find((customNode) => customNode.name === type);
626
+ if (customNode) {
627
+ onEdit(customNode.defaultValue, path);
628
+ }
629
+ else {
630
+ setValue(convertValue(value, type));
631
+ setDataType(type);
632
+ }
539
633
  };
540
634
  const logError = (errorString) => {
541
635
  setError(errorString);
@@ -547,10 +641,10 @@ const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard,
547
641
  let newValue;
548
642
  switch (dataType) {
549
643
  case 'object':
550
- newValue = {};
644
+ newValue = { [translate('DEFAULT_NEW_KEY')]: value };
551
645
  break;
552
646
  case 'array':
553
- newValue = value !== null ? [value] : [];
647
+ newValue = value !== null ? value : [];
554
648
  break;
555
649
  case 'number':
556
650
  const n = Number(value);
@@ -567,9 +661,27 @@ const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard,
567
661
  logError(error);
568
662
  });
569
663
  };
664
+ const handleEditKey = (newKey) => {
665
+ setIsEditingKey(false);
666
+ if (!parentData)
667
+ return;
668
+ const parentPath = path.slice(0, -1);
669
+ if (!newKey)
670
+ return;
671
+ const newData = Object.fromEntries(Object.entries(parentData).map(([key, val]) => (key === name ? [newKey, val] : [key, val])));
672
+ onEdit(newData, parentPath);
673
+ };
674
+ const handleKeyPress = (e) => {
675
+ if (e.key === 'Enter')
676
+ handleEditKey(e.target.value);
677
+ else if (e.key === 'Escape')
678
+ handleCancel();
679
+ };
570
680
  const handleCancel = () => {
571
681
  setIsEditing(false);
682
+ setIsEditingKey(false);
572
683
  setValue(data);
684
+ setDataType(getDataType(data));
573
685
  };
574
686
  const handleDelete = () => {
575
687
  onDelete(value, path).then((error) => {
@@ -580,20 +692,47 @@ const ValueNodeWrapper = ({ data, name, path, onEdit, onDelete, enableClipboard,
580
692
  const filterProps = { key: name, path, level: path.length, value: data, size: null };
581
693
  const canEdit = useMemo(() => !restrictEditFilter(filterProps), [filterProps]);
582
694
  const canDelete = useMemo(() => !restrictDeleteFilter(filterProps), [filterProps]);
695
+ const isArray = typeof path.slice(-1)[0] === 'number';
696
+ const canEditKey = !isArray && canEdit && canDelete;
583
697
  const inputProps = {
584
698
  value,
699
+ parentData,
585
700
  setValue,
586
701
  isEditing,
587
- setIsEditing,
702
+ setIsEditing: canEdit ? () => setIsEditing(true) : () => { },
588
703
  handleEdit,
589
704
  handleCancel,
590
705
  path,
591
706
  stringTruncate,
707
+ translate,
592
708
  };
593
- return (jsx("div", Object.assign({ className: "jer-component jer-value-component", style: { marginLeft: `${indent / 2}em` } }, { children: jsxs("div", Object.assign({ className: "jer-value-main-row", style: {
709
+ const { CustomNode, customNodeProps, hideKey } = getCustomNode(customNodeDefinitions, {
710
+ key: name,
711
+ path,
712
+ level: path.length,
713
+ value: data,
714
+ size: 0,
715
+ });
716
+ const allDataTypes = [
717
+ ...customNodeDefinitions
718
+ .filter(({ showInTypesSelector = false }) => showInTypesSelector)
719
+ .map(({ name }) => name),
720
+ ...DataTypes,
721
+ ];
722
+ const allowedDataTypes = useMemo(() => {
723
+ if (typeof restrictTypeSelection === 'boolean')
724
+ return restrictTypeSelection ? [] : allDataTypes;
725
+ if (Array.isArray(restrictTypeSelection))
726
+ return restrictTypeSelection;
727
+ const result = restrictTypeSelection(filterProps);
728
+ if (typeof result === 'boolean')
729
+ return result ? [] : allDataTypes;
730
+ return result;
731
+ }, [filterProps, restrictTypeSelection]);
732
+ return CustomNode ? (jsx(CustomNodeWrapper, { name: name, hideKey: hideKey, indent: indent, children: jsx(CustomNode, Object.assign({ customProps: customNodeProps }, props)) })) : (jsx("div", { className: "jer-component jer-value-component", style: { marginLeft: `${indent / 2}em` }, children: jsxs("div", { className: "jer-value-main-row", style: {
594
733
  flexWrap: name.length > 10 ? 'wrap' : 'nowrap',
595
- } }, { children: [showArrayIndices && (jsxs("label", Object.assign({ 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 }) }, { children: [name, ":", ' '] }))), jsxs("div", Object.assign({ className: "jer-value-and-buttons" }, { children: [jsx("div", Object.assign({ className: "jer-input-component" }, { children: getInputComponent(dataType, inputProps) })), isEditing ? (jsx(InputButtons, { onOk: handleEdit, onCancel: handleCancel })) : (dataType !== 'invalid' &&
596
- !error && (jsx(EditButtons, { startEdit: canEdit ? () => setIsEditing(true) : undefined, handleDelete: canDelete ? handleDelete : undefined, data: data, enableClipboard: enableClipboard, name: name, path: path, translate: translate }))), isEditing && (jsxs("div", Object.assign({ className: "jer-select" }, { children: [jsx("select", Object.assign({ name: `${name}-type-select`, className: "jer-type-select", onChange: (e) => handleChangeDataType(e.target.value), value: dataType }, { children: DataTypes.map((type) => (jsx("option", Object.assign({ value: type }, { children: type }), type))) })), jsx("span", { className: "focus" })] }))), !isEditing && error && (jsx("span", Object.assign({ className: "jer-error-slug", style: styles.error }, { children: error })))] }))] })) })));
734
+ }, children: [showLabel && !isEditingKey && (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 && (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` } })), jsxs("div", { className: "jer-value-and-buttons", children: [jsx("div", { className: "jer-input-component", children: getInputComponent(dataType, inputProps) }), isEditing ? (jsx(InputButtons, { onOk: handleEdit, onCancel: handleCancel })) : (dataType !== 'invalid' &&
735
+ !error && (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 && (jsxs("div", { className: "jer-select", children: [jsx("select", { name: `${name}-type-select`, className: "jer-type-select", onChange: (e) => handleChangeDataType(e.target.value), value: dataType, children: allowedDataTypes.map((type) => (jsx("option", { value: type, children: type }, type))) }), jsx("span", { className: "focus" })] })), !isEditing && error && (jsx("span", { className: "jer-error-slug", style: styles.error, children: error }))] })] }) }));
597
736
  };
598
737
  const getDataType = (value) => {
599
738
  if (typeof value === 'string')
@@ -618,7 +757,7 @@ const getInputComponent = (dataType, inputProps) => {
618
757
  case 'null':
619
758
  return jsx(NullValue, Object.assign({}, inputProps));
620
759
  case 'object':
621
- return jsx(ObjectValue, Object.assign({}, inputProps));
760
+ return jsx(ObjectValue, Object.assign({}, inputProps, { value: value }));
622
761
  case 'array':
623
762
  return jsx(ArrayValue, Object.assign({}, inputProps));
624
763
  default:
@@ -637,7 +776,7 @@ const convertValue = (value, type) => {
637
776
  case 'null':
638
777
  return null;
639
778
  case 'object':
640
- return {};
779
+ return value;
641
780
  case 'array':
642
781
  return [value];
643
782
  default:
@@ -647,15 +786,19 @@ const convertValue = (value, type) => {
647
786
 
648
787
  const isCollection = (value) => value !== null && typeof value == 'object';
649
788
  const CollectionNode = (_a) => {
650
- var { data, path, name } = _a, props = __rest(_a, ["data", "path", "name"]);
789
+ var { data, path, name, parentData, showCollectionCount } = _a, props = __rest(_a, ["data", "path", "name", "parentData", "showCollectionCount"]);
651
790
  const { styles } = useTheme();
652
- const { onEdit, onAdd, onDelete, restrictEditFilter, restrictDeleteFilter, restrictAddFilter, collapseFilter, enableClipboard, indent, keySort, showArrayIndices, defaultValue, translate, } = props;
791
+ const { onEdit, onAdd, onDelete, restrictEditFilter, restrictDeleteFilter, restrictAddFilter, collapseFilter, enableClipboard, indent, keySort, showArrayIndices, defaultValue, translate, customNodeDefinitions, } = props;
653
792
  const [isEditing, setIsEditing] = useState(false);
793
+ const [isEditingKey, setIsEditingKey] = useState(false);
654
794
  const [stringifiedValue, setStringifiedValue] = useState(JSON.stringify(data, null, 2));
655
795
  const [error, setError] = useState(null);
656
796
  const collectionSize = Object.keys(data).length;
657
797
  const filterProps = { key: name, path, level: path.length, value: data, size: collectionSize };
658
- const [collapsed, setCollapsed] = useState(collapseFilter(filterProps));
798
+ const startCollapsed = collapseFilter(filterProps);
799
+ const [collapsed, setCollapsed] = useState(startCollapsed);
800
+ const hasBeenOpened = useRef(!startCollapsed);
801
+ const [isAnimating, setIsAnimating] = useState(false);
659
802
  useEffect(() => {
660
803
  setStringifiedValue(JSON.stringify(data, null, 2));
661
804
  }, [data]);
@@ -671,6 +814,14 @@ const CollectionNode = (_a) => {
671
814
  else if (e.key === 'Escape')
672
815
  handleCancel();
673
816
  };
817
+ const handleCollapse = () => {
818
+ if (!isEditing) {
819
+ setIsAnimating(true);
820
+ hasBeenOpened.current = true;
821
+ setCollapsed(!collapsed);
822
+ setTimeout(() => setIsAnimating(false), 500);
823
+ }
824
+ };
674
825
  const showError = (errorString) => {
675
826
  setError(errorString);
676
827
  setTimeout(() => setError(null), ERROR_DISPLAY_TIME);
@@ -693,6 +844,22 @@ const CollectionNode = (_a) => {
693
844
  return;
694
845
  }
695
846
  };
847
+ const handleEditKey = (newKey) => {
848
+ setIsEditingKey(false);
849
+ if (!parentData)
850
+ return;
851
+ const parentPath = path.slice(0, -1);
852
+ if (!newKey)
853
+ return;
854
+ const newData = Object.fromEntries(Object.entries(parentData).map(([key, val]) => (key === name ? [newKey, val] : [key, val])));
855
+ onEdit(newData, parentPath);
856
+ };
857
+ const handleKeyPressKeyEdit = (e) => {
858
+ if (e.key === 'Enter')
859
+ handleEditKey(e.target.value);
860
+ else if (e.key === 'Escape')
861
+ handleCancel();
862
+ };
696
863
  const handleAdd = (key) => {
697
864
  setCollapsed(false);
698
865
  if (collectionType === 'array') {
@@ -727,7 +894,10 @@ const CollectionNode = (_a) => {
727
894
  const canEdit = useMemo(() => !restrictEditFilter(filterProps), [filterProps]);
728
895
  const canDelete = useMemo(() => !restrictDeleteFilter(filterProps), [filterProps]);
729
896
  const canAdd = useMemo(() => !restrictAddFilter(filterProps), [filterProps]);
730
- const showLabel = showArrayIndices || !(typeof path.slice(-1)[0] === 'number');
897
+ const canEditKey = parentData !== null && canEdit && canAdd && canDelete;
898
+ const isArray = typeof path.slice(-1)[0] === 'number';
899
+ const showLabel = showArrayIndices || !isArray;
900
+ const showCount = showCollectionCount === 'when-closed' ? collapsed : showCollectionCount;
731
901
  const keyValueArray = Object.entries(data).map(([key, value]) => [
732
902
  collectionType === 'array' ? Number(key) : key,
733
903
  value,
@@ -735,21 +905,27 @@ const CollectionNode = (_a) => {
735
905
  if (keySort && collectionType === 'object')
736
906
  keyValueArray.sort(typeof keySort === 'function' ? (a, b) => keySort(a[0], b[0]) : undefined);
737
907
  const numOfLines = JSON.stringify(data, null, 2).split('\n').length;
738
- return (jsxs("div", Object.assign({ className: "jer-component fb-collection-component", style: { marginLeft: `${path.length === 0 ? 0 : indent / 2}em` } }, { children: [jsxs("div", Object.assign({ className: "jer-collection-header-row" }, { children: [jsx("div", Object.assign({ onClick: () => {
739
- if (!isEditing)
740
- setCollapsed(!collapsed);
741
- } }, { children: jsx(Icon, { name: "chevron", rotate: collapsed }) })), jsxs("div", Object.assign({ className: "jer-collection-name" }, { children: [jsx("span", Object.assign({ style: styles.property }, { children: showLabel ? `${name}:` : null })), !isEditing && (jsx("span", Object.assign({ className: "jer-brackets", style: styles.bracket }, { children: brackets.open })))] })), !isEditing && (jsx("div", Object.assign({ className: "jer-collection-item-count", style: styles.itemCount }, { children: collectionSize === 1
742
- ? translate('ITEM_SINGLE', 1)
743
- : translate('ITEMS_MULTIPLE', collectionSize) }))), jsx("div", Object.assign({ className: `jer-brackets${collapsed ? ' jer-visible' : ' jer-hidden'}`, style: styles.bracket }, { children: brackets.close })), !isEditing && (jsx(EditButtons, { startEdit: canEdit
744
- ? () => {
745
- setIsEditing(true);
746
- setCollapsed(false);
747
- }
748
- : undefined, handleAdd: canAdd ? handleAdd : undefined, handleDelete: canDelete ? handleDelete : undefined, enableClipboard: enableClipboard, type: collectionType, data: data, name: name, path: path, translate: translate }))] })), jsxs("div", Object.assign({ className: 'jer-collection-inner', style: {
749
- maxHeight: collapsed ? 0 : !isEditing ? `${numOfLines * 4}em` : undefined,
750
- overflowY: collapsed ? 'hidden' : 'visible',
751
- transition: `max-height ${transitionTime}`,
752
- } }, { children: [isEditing ? (jsx("div", Object.assign({ className: "jer-collection-text-edit" }, { children: jsxs("div", { children: [jsx(AutogrowTextArea, { className: "jer-collection-text-area", name: path.join('.'), value: stringifiedValue, setValue: setStringifiedValue, isEditing: isEditing, handleKeyPress: handleKeyPress }), jsx("div", Object.assign({ className: "jer-collection-input-button-row" }, { children: jsx(InputButtons, { onOk: handleEdit, onCancel: handleCancel, isCollection: true }) }))] }) }))) : (jsx(Fragment, { children: keyValueArray.map(([key, value]) => (jsx("div", Object.assign({ className: "jer-collection-element" }, { children: isCollection(value) ? (jsx(CollectionNode, Object.assign({ data: value, path: [...path, key], name: key }, props), key)) : (jsx(ValueNodeWrapper, Object.assign({ data: value, path: [...path, key], name: key }, props, { showArrayIndices: collectionType === 'object' ? true : showArrayIndices }), key)) }), key))) })), jsx("div", Object.assign({ className: isEditing ? 'jer-collection-error-row' : 'jer-collection-error-row-edit' }, { children: error && (jsx("span", Object.assign({ className: "jer-error-slug", style: styles.error }, { children: error }))) }))] })), !collapsed && !isEditing && (jsx("div", Object.assign({ className: "jer-brackets", style: styles.bracket }, { children: brackets.close })))] })));
908
+ const { CustomNode, customNodeProps, hideKey } = getCustomNode(customNodeDefinitions, {
909
+ key: name,
910
+ path,
911
+ level: path.length,
912
+ value: data,
913
+ size: Object.keys(data).length,
914
+ });
915
+ return (jsx("div", { className: "jer-component fb-collection-component", style: { marginLeft: `${path.length === 0 ? 0 : indent / 2}em` }, children: CustomNode ? (jsx(CustomNodeWrapper, { name: name, hideKey: hideKey, children: jsx(CustomNode, Object.assign({ data: data, path: path, name: name, parentData: parentData, customProps: customNodeProps }, props)) })) : (jsxs(Fragment, { children: [jsxs("div", { className: "jer-collection-header-row", children: [jsx("div", { onClick: handleCollapse, children: jsx(Icon, { name: "chevron", rotate: collapsed }) }), jsxs("div", { className: "jer-collection-name", children: [!isEditingKey && (jsx("span", { style: styles.property, onDoubleClick: () => canEditKey && setIsEditingKey(true), children: showLabel && name !== '' && name !== undefined ? `${name}:` : null })), isEditingKey && (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 && (jsx("span", { className: "jer-brackets", style: styles.bracket, children: brackets.open }))] }), !isEditing && showCollectionCount && (jsx("div", { className: `jer-collection-item-count${showCount ? ' jer-visible' : ' jer-hidden'}`, style: styles.itemCount, children: collectionSize === 1
916
+ ? translate('ITEM_SINGLE', 1)
917
+ : translate('ITEMS_MULTIPLE', collectionSize) })), jsx("div", { className: `jer-brackets${collapsed ? ' jer-visible' : ' jer-hidden'}`, style: styles.bracket, children: brackets.close }), !isEditing && (jsx(EditButtons, { startEdit: canEdit
918
+ ? () => {
919
+ setIsEditing(true);
920
+ setCollapsed(false);
921
+ }
922
+ : undefined, handleAdd: canAdd ? handleAdd : undefined, handleDelete: canDelete ? handleDelete : undefined, enableClipboard: enableClipboard, type: collectionType, data: data, name: name, path: path, translate: translate }))] }), jsxs("div", { className: 'jer-collection-inner', style: {
923
+ maxHeight: collapsed ? 0 : !isEditing ? `${numOfLines * 3}em` : undefined,
924
+ overflowY: collapsed || isAnimating ? 'hidden' : 'visible',
925
+ transition: `max-height ${transitionTime}`,
926
+ }, children: [isEditing ? (jsx("div", { className: "jer-collection-text-edit", children: jsxs("div", { children: [jsx(AutogrowTextArea, { className: "jer-collection-text-area", name: path.join('.'), value: stringifiedValue, setValue: setStringifiedValue, isEditing: isEditing, handleKeyPress: handleKeyPress }), jsx("div", { className: "jer-collection-input-button-row", children: jsx(InputButtons, { onOk: handleEdit, onCancel: handleCancel, isCollection: true }) })] }) })) : (jsx(Fragment, { children: !hasBeenOpened.current
927
+ ? null
928
+ : keyValueArray.map(([key, value]) => (jsx("div", { className: "jer-collection-element", children: isCollection(value) ? (jsx(CollectionNode, Object.assign({ data: value, parentData: data, path: [...path, key], name: key, showCollectionCount: showCollectionCount }, props), key)) : (jsx(ValueNodeWrapper, Object.assign({ data: value, parentData: data, path: [...path, key], name: key }, props, { showLabel: collectionType === 'object' ? true : showArrayIndices }), key)) }, key))) })), jsx("div", { className: isEditing ? 'jer-collection-error-row' : 'jer-collection-error-row-edit', children: error && (jsx("span", { className: "jer-error-slug", style: styles.error, children: error })) }), jsx("div", { className: "jer-brackets", style: styles.bracket, children: brackets.close })] })] })) }));
753
929
  };
754
930
 
755
931
  const localisedStrings = {
@@ -761,6 +937,8 @@ const localisedStrings = {
761
937
  ERROR_UPDATE: 'Update unsuccessful',
762
938
  ERROR_DELETE: 'Delete unsuccessful',
763
939
  ERROR_ADD: 'Adding node unsuccessful',
940
+ DEFAULT_STRING: 'New data!',
941
+ DEFAULT_NEW_KEY: 'key',
764
942
  };
765
943
  const translate = (translations, key, count) => {
766
944
  const string = key in translations ? translations[key] : localisedStrings[key];
@@ -768,7 +946,7 @@ const translate = (translations, key, count) => {
768
946
  };
769
947
  const getTranslateFunction = (translations) => (key, count) => translate(translations, key, count);
770
948
 
771
- 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 = 600, stringTruncate = 250, translations = {}, className, }) => {
949
+ 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 = [], }) => {
772
950
  const [data, setData] = useState(srcData);
773
951
  const { styles, setTheme, setIcons } = useTheme();
774
952
  const collapseFilter = useCallback(getFilterFunction(collapse), [collapse]);
@@ -850,10 +1028,12 @@ const Editor = ({ data: srcData, rootName = 'root', onUpdate, onEdit: srcEdit =
850
1028
  onEdit,
851
1029
  onDelete,
852
1030
  onAdd,
1031
+ showCollectionCount,
853
1032
  collapseFilter,
854
1033
  restrictEditFilter,
855
1034
  restrictDeleteFilter,
856
1035
  restrictAddFilter,
1036
+ restrictTypeSelection,
857
1037
  enableClipboard,
858
1038
  keySort,
859
1039
  showArrayIndices,
@@ -861,10 +1041,12 @@ const Editor = ({ data: srcData, rootName = 'root', onUpdate, onEdit: srcEdit =
861
1041
  defaultValue,
862
1042
  stringTruncate,
863
1043
  translate,
1044
+ customNodeDefinitions,
1045
+ parentData: null,
864
1046
  };
865
1047
  if (!styles)
866
1048
  return null;
867
- return (jsx("div", Object.assign({ className: 'jer-editor-container ' + className, style: Object.assign(Object.assign({}, styles.container), { minWidth, maxWidth }) }, { children: isCollection(data) && jsx(CollectionNode, Object.assign({ data: data, path: [] }, otherProps)) })));
1049
+ return (jsx("div", { className: 'jer-editor-container ' + className, style: Object.assign(Object.assign({}, styles.container), { minWidth, maxWidth }), children: isCollection(data) ? (jsx(CollectionNode, Object.assign({ data: data, path: [] }, otherProps))) : (jsx(ValueNodeWrapper, Object.assign({ data: data, path: [], showLabel: true }, otherProps))) }));
868
1050
  };
869
1051
  const JsonEditor = (props) => (jsx(ThemeProvider, { children: jsx(Editor, Object.assign({}, props)) }));
870
1052
  const updateDataObject = (data, path, newValue, action) => {
@@ -893,4 +1075,4 @@ const getFilterFunction = (propValue) => {
893
1075
  return propValue;
894
1076
  };
895
1077
 
896
- export { JsonEditor, JsonEditor as default, themes };
1078
+ export { JsonEditor, themes };