From 5b5bb7ab365e7a4290cea2d0f581792dbb7969f6 Mon Sep 17 00:00:00 2001 From: Carl Smith <5456533+CarlosNZ@users.noreply.github.com> Date: Mon, 9 Jun 2025 23:59:47 +1200 Subject: [PATCH 01/10] Implement in Demo, Improvements to Markdown renderer --- .../components/Markdown/component.tsx | 16 ++-- .../components/Markdown/definition.ts | 4 +- custom-component-library/src/App.tsx | 6 +- .../{components => src}/data.ts | 7 +- demo/src/App.tsx | 15 +++- demo/src/demoData/dataDefinitions.tsx | 76 ++++++++++++++++++- demo/src/style.css | 34 +++++++++ 7 files changed, 140 insertions(+), 18 deletions(-) rename custom-component-library/{components => src}/data.ts (87%) diff --git a/custom-component-library/components/Markdown/component.tsx b/custom-component-library/components/Markdown/component.tsx index 518ead60..a26cba4a 100644 --- a/custom-component-library/components/Markdown/component.tsx +++ b/custom-component-library/components/Markdown/component.tsx @@ -6,15 +6,11 @@ import React from 'react' import { type CustomNodeProps } from '@json-edit-react' -import Markdown from 'react-markdown' +import Markdown, { Options } from 'react-markdown' -export interface LinkProps { - linkStyles?: React.CSSProperties - stringTruncate?: number - [key: string]: unknown -} +export type ReactMarkdownProps = Options -export const MarkdownComponent: React.FC> = (props) => { +export const MarkdownComponent: React.FC> = (props) => { const { setIsEditing, getStyles, nodeData } = props const styles = getStyles('string', nodeData) @@ -24,10 +20,10 @@ export const MarkdownComponent: React.FC> = (props) = if (e.getModifierState('Control') || e.getModifierState('Meta')) setIsEditing(true) }} onDoubleClick={() => setIsEditing(true)} - style={styles} + style={{ ...styles }} + className="jer-markdown-block" > - {/* TO-DO: Style over-rides */} - {nodeData.value as string} + {nodeData.value as string} ) } diff --git a/custom-component-library/components/Markdown/definition.ts b/custom-component-library/components/Markdown/definition.ts index 6696bb9c..d8ff2edf 100644 --- a/custom-component-library/components/Markdown/definition.ts +++ b/custom-component-library/components/Markdown/definition.ts @@ -1,7 +1,7 @@ import { type CustomNodeDefinition } from '@json-edit-react' -import { MarkdownComponent, LinkProps } from './component' +import { MarkdownComponent, ReactMarkdownProps } from './component' -export const MarkdownNodeDefinition: CustomNodeDefinition = { +export const MarkdownNodeDefinition: CustomNodeDefinition = { condition: () => false, // Over-ride this for specific cases element: MarkdownComponent, // customNodeProps: {}, diff --git a/custom-component-library/src/App.tsx b/custom-component-library/src/App.tsx index d37a8cb9..fd85bc07 100644 --- a/custom-component-library/src/App.tsx +++ b/custom-component-library/src/App.tsx @@ -14,16 +14,20 @@ import { MarkdownNodeDefinition, EnhancedLinkCustomNodeDefinition, } from '../components' -import { testData } from '../components/data' +import { testData } from './data' import { JsonData, JsonEditor } from '@json-edit-react' if (testData?.['Date & Time']) { // @ts-expect-error redefine after initialisation testData['Date & Time'].Date = STORE_DATE_AS_DATE_OBJECT ? new Date() : new Date().toISOString() + // @ts-expect-error adding property testData['Date & Time'].info = STORE_DATE_AS_DATE_OBJECT ? 'Date is stored a JS Date object. To use ISO string, set STORE_DATE_AS_DATE_OBJECT to false in App.tsx.' : 'Date is stored as ISO string. To use JS Date objects, set STORE_DATE_AS_DATE_OBJECT to true in App.tsx.' + + // @ts-expect-error only used in Demo app + delete testData['Date & Time']['Date Object'] } type TestData = typeof testData diff --git a/custom-component-library/components/data.ts b/custom-component-library/src/data.ts similarity index 87% rename from custom-component-library/components/data.ts rename to custom-component-library/src/data.ts index dda0e896..b274a15e 100644 --- a/custom-component-library/components/data.ts +++ b/custom-component-library/src/data.ts @@ -14,6 +14,7 @@ export const testData = { - DateObject - Undefined - Markdown + - "Enhanced" link - BigInt - BooleanToggle - NaN @@ -21,19 +22,21 @@ export const testData = { Click [here](https://github.com/CarlosNZ/json-edit-react/blob/main/custom-component-library/README.md) for more info `, + 'Simpler boolean toggle': true, 'Active Links': { Url: 'https://carlosnz.github.io/json-edit-react/', 'Long URL': 'https://www.google.com/maps/place/Sky+Tower/@-36.8465603,174.7609398,818m/data=!3m1!1e3!4m6!3m5!1s0x6d0d47f06d4bdc25:0x2d1b5c380ad9387!8m2!3d-36.848448!4d174.762191!16zL20vMDFuNXM2?entry=ttu&g_ep=EgoyMDI1MDQwOS4wIKXMDSoASAFQAw%3D%3D', 'Enhanced Link': { - text: 'This link displays custom text', + text: 'This link displays custom text — try editing me!', url: 'https://github.com/CarlosNZ/json-edit-react/tree/main/custom-component-library#custom-component-library', }, }, 'Date & Time': { Date: new Date().toISOString(), + 'Date Object': new Date(), 'Show Time in Date?': true, - info: 'Replaced in App.tsx', + // info: 'Inserted in App.tsx', }, 'Non-JSON types': { diff --git a/demo/src/App.tsx b/demo/src/App.tsx index dfb95925..cacf4534 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, lazy, Suspense } from 'react' +import { useEffect, useRef, lazy, Suspense, useMemo } from 'react' import { useSearch, useLocation } from 'wouter' import JSON5 from 'json5' import { @@ -153,6 +153,17 @@ function App() { // } // }, []) + const customNodeDefinitions = + selectedDataSet === 'customComponentLibrary' && + typeof data === 'object' && + (data as any)?.['Date & Time']?.['Show Time in Date?'] && + Array.isArray(dataDefinition.customNodeDefinitions) + ? [ + { ...dataDefinition.customNodeDefinitions[0], customNodeProps: { showTime: true } }, + ...dataDefinition.customNodeDefinitions.slice(1), + ] + : dataDefinition.customNodeDefinitions + const updateState = (patch: Partial) => setState({ ...state, ...patch }) const toggleState = (field: keyof AppState) => updateState({ [field]: !state[field] }) @@ -496,7 +507,7 @@ function App() { maxWidth="min(670px, 90vw)" className="block-shadow" stringTruncate={90} - customNodeDefinitions={dataDefinition?.customNodeDefinitions} + customNodeDefinitions={customNodeDefinitions} // customNodeDefinitions={[ // { // condition: ({ key }) => key === 'string', diff --git a/demo/src/demoData/dataDefinitions.tsx b/demo/src/demoData/dataDefinitions.tsx index 4f3c0acf..de2cb407 100644 --- a/demo/src/demoData/dataDefinitions.tsx +++ b/demo/src/demoData/dataDefinitions.tsx @@ -4,7 +4,16 @@ import { Flex, Box, Link, Text, UnorderedList, ListItem } from '@chakra-ui/react import { DatePickerDefinition, LinkCustomNodeDefinition, + DateObjectDefinition, + UndefinedDefinition, + BooleanToggleDefinition, + NanDefinition, + SymbolDefinition, + BigIntDefinition, + MarkdownNodeDefinition, + EnhancedLinkCustomNodeDefinition, } from '../../../custom-component-library/components' +import { testData } from '../../../custom-component-library/src/data' import { CustomNodeDefinition, FilterFunction, @@ -33,6 +42,9 @@ const ajv = new Ajv() const validateJsonSchema = ajv.compile(jsonSchema) const validateCustomNodes = ajv.compile(customNodesSchema) +// @ts-expect-error only used in Custom component demo app +delete testData['Date & Time']['Date'] + export interface DemoData { name: string description: React.JSX.Element @@ -65,7 +77,7 @@ export interface DemoData { export const demoDataDefinitions: Record = { intro: { - name: '📘 Intro', + name: '📣 Intro', description: ( Play around with the JSON structure, and test out various options. @@ -778,4 +790,66 @@ export const demoDataDefinitions: Record = { }, customTextEditorAvailable: true, }, + customComponentLibrary: { + name: '📚 Custom Component Library', + description: ( + + Play around with the JSON structure, and test out various options. + + There are a range of different demo data sets to play with, showcasing specific features + in each one (over and above the modifiable options above). The definitions for all demo + data displays can be found in the repo{' '} + + here + + . + + Incorporate into your own React project: + + + npm i json-edit-react +
+ or: +
+ yarn add json-edit-react +
+
+
+ ), + rootName: 'components', + collapse: 2, + data: testData, + customNodeDefinitions: [ + // Must keep this one first as we override it by index in App.tsx + { + ...DateObjectDefinition, + customNodeProps: { showTime: false }, + }, + LinkCustomNodeDefinition, + EnhancedLinkCustomNodeDefinition, + UndefinedDefinition, + BooleanToggleDefinition, + NanDefinition, + SymbolDefinition, + BigIntDefinition, + { + ...MarkdownNodeDefinition, + condition: ({ key }) => key === 'Markdown', + }, + { + ...MarkdownNodeDefinition, + condition: ({ key }) => key === 'Intro', + hideKey: true, + customNodeProps: { + components: { + a: ({ _, ...props }) => , + }, + }, + }, + ], + customTextEditorAvailable: true, + }, } diff --git a/demo/src/style.css b/demo/src/style.css index 479345f8..2a685a18 100644 --- a/demo/src/style.css +++ b/demo/src/style.css @@ -63,3 +63,37 @@ footer { align-items: center; justify-content: center; } + +/* For the "Markdown" block in the "Custom Component Library" data set */ +.jer-markdown-block h1, +h2, +h3 { + font-family: Work Sans, sans-serif; + font-weight: bold; + line-height: 1.4em; + /* color: #ea3788; */ +} + +.jer-markdown-block h1 { + font-size: 2em; +} + +.jer-markdown-block h2 { + font-size: 1.5em; +} + +.jer-markdown-block h3 { + font-size: 1.2em; +} + +.jer-markdown-block { + line-height: 1.3em; +} + +.jer-markdown-block ul { + margin: 0.4em 2em; + /* color: black; */ +} +.jer-markdown-block p { + margin: 0.5em 0; +} From 2912909d7d80031089bc22f39b1563f45735947d Mon Sep 17 00:00:00 2001 From: Carl Smith <5456533+CarlosNZ@users.noreply.github.com> Date: Wed, 11 Jun 2025 14:11:44 +1200 Subject: [PATCH 02/10] Fix errors --- demo/eslint.config.js | 1 + demo/src/App.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/demo/eslint.config.js b/demo/eslint.config.js index b8f09d93..bd3f06cb 100644 --- a/demo/eslint.config.js +++ b/demo/eslint.config.js @@ -20,6 +20,7 @@ export default tseslint.config( rules: { ...reactHooks.configs.recommended.rules, 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], }, } ) diff --git a/demo/src/App.tsx b/demo/src/App.tsx index cacf4534..198c0ecb 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, lazy, Suspense, useMemo } from 'react' +import { useEffect, useRef, lazy, Suspense } from 'react' import { useSearch, useLocation } from 'wouter' import JSON5 from 'json5' import { @@ -156,7 +156,7 @@ function App() { const customNodeDefinitions = selectedDataSet === 'customComponentLibrary' && typeof data === 'object' && - (data as any)?.['Date & Time']?.['Show Time in Date?'] && + (data as Record>)?.['Date & Time']?.['Show Time in Date?'] && Array.isArray(dataDefinition.customNodeDefinitions) ? [ { ...dataDefinition.customNodeDefinitions[0], customNodeProps: { showTime: true } }, From bba800bdd1fa15b549e48fb011c5160b51e2ef2e Mon Sep 17 00:00:00 2001 From: Carl Smith <5456533+CarlosNZ@users.noreply.github.com> Date: Thu, 12 Jun 2025 11:46:07 +1200 Subject: [PATCH 03/10] Lazy loading --- .../components/DatePicker/component.tsx | 54 ++++++++++++------- .../components/Markdown/component.tsx | 19 ++++--- .../components/Markdown/definition.ts | 4 +- .../components/_common/Loading.tsx | 6 +++ .../components/_common/style.css | 12 +++++ demo/src/App.tsx | 3 +- demo/src/demoData/dataDefinitions.tsx | 1 + demo/vite.config.ts | 1 + 8 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 custom-component-library/components/_common/Loading.tsx create mode 100644 custom-component-library/components/_common/style.css diff --git a/custom-component-library/components/DatePicker/component.tsx b/custom-component-library/components/DatePicker/component.tsx index 547c80b9..930d4536 100644 --- a/custom-component-library/components/DatePicker/component.tsx +++ b/custom-component-library/components/DatePicker/component.tsx @@ -8,19 +8,22 @@ * rather than requiring the user to edit the ISO string directly. */ -import React from 'react' -import DatePicker from 'react-datepicker' +import React, { lazy, Suspense } from 'react' import { Button } from './Button' import { CustomNodeProps } from '@json-edit-react' // Styles import 'react-datepicker/dist/react-datepicker.css' import './style.css' +import { Loading } from '../_common/Loading' + +const DatePicker = lazy(() => import('react-datepicker')) export interface DatePickerCustomProps { dateFormat?: string dateTimeFormat?: string showTime?: boolean + loadingText?: string } export const DateTimePicker: React.FC> = ({ @@ -39,6 +42,7 @@ export const DateTimePicker: React.FC> = dateFormat = 'MMM d, yyyy', dateTimeFormat = 'MMM d, yyyy h:mm aa', showTime = true, + loadingText = 'Loading Date Picker', } = customNodeProps ?? {} const date = new Date(value as string) @@ -54,26 +58,38 @@ export const DateTimePicker: React.FC> = // at all when viewing (and so will show raw ISO strings). However, we've // defined an alternative here too, when showOnView == true, in which case // the date/time string is shown as a localised date/time. - date && setValue(date.toISOString())} - open={true} - onKeyDown={handleKeyPress} + + + + } > -
- {/* These buttons are not really necessary -- you can either use the + date && setValue(date.toISOString())} + open={true} + onKeyDown={handleKeyPress} + > +
+ {/* These buttons are not really necessary -- you can either use the standard Ok/Cancel icons, or keyboard Enter/Esc, but shown for demo purposes */} -
-
+
+
+ ) : (
import('react-markdown')) -export const MarkdownComponent: React.FC> = (props) => { - const { setIsEditing, getStyles, nodeData } = props +export interface MarkdownCustomProps extends Options { + loadingText?: string +} + +export const MarkdownComponent: React.FC> = (props) => { + const { setIsEditing, getStyles, nodeData, customNodeProps } = props const styles = getStyles('string', nodeData) return ( @@ -23,7 +28,9 @@ export const MarkdownComponent: React.FC> = style={{ ...styles }} className="jer-markdown-block" > - {nodeData.value as string} + }> + {nodeData.value as string} +
) } diff --git a/custom-component-library/components/Markdown/definition.ts b/custom-component-library/components/Markdown/definition.ts index d8ff2edf..17449e77 100644 --- a/custom-component-library/components/Markdown/definition.ts +++ b/custom-component-library/components/Markdown/definition.ts @@ -1,7 +1,7 @@ import { type CustomNodeDefinition } from '@json-edit-react' -import { MarkdownComponent, ReactMarkdownProps } from './component' +import { MarkdownComponent, MarkdownCustomProps } from './component' -export const MarkdownNodeDefinition: CustomNodeDefinition = { +export const MarkdownNodeDefinition: CustomNodeDefinition = { condition: () => false, // Over-ride this for specific cases element: MarkdownComponent, // customNodeProps: {}, diff --git a/custom-component-library/components/_common/Loading.tsx b/custom-component-library/components/_common/Loading.tsx new file mode 100644 index 00000000..45629148 --- /dev/null +++ b/custom-component-library/components/_common/Loading.tsx @@ -0,0 +1,6 @@ +import React from 'react' +import './style.css' + +export const Loading: React.FC<{ text?: string }> = ({ text = 'Loading' }) => { + return
{text}...
+} diff --git a/custom-component-library/components/_common/style.css b/custom-component-library/components/_common/style.css new file mode 100644 index 00000000..a827f8af --- /dev/null +++ b/custom-component-library/components/_common/style.css @@ -0,0 +1,12 @@ +.jer-simple-loader { + width: fit-content; + font-style: italic; + clip-path: inset(0 3ch 0 0); + animation: l4 1s steps(4) infinite; + line-height: 1.2em; +} +@keyframes l4 { + to { + clip-path: inset(0 -1ch 0 0); + } +} diff --git a/demo/src/App.tsx b/demo/src/App.tsx index 198c0ecb..714be0e5 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -48,6 +48,7 @@ import { demoDataDefinitions } from './demoData' import { useDatabase } from './useDatabase' import './style.css' import { getLineHeight, truncate } from './helpers' +import { Loading } from '../../custom-component-library/components/_common/Loading' const CodeEditor = lazy(() => import('./CodeEditor')) const SourceIndicator = lazy(() => import('./SourceIndicator')) @@ -570,7 +571,7 @@ function App() { - Loading code editor... + } > diff --git a/demo/src/demoData/dataDefinitions.tsx b/demo/src/demoData/dataDefinitions.tsx index 28030c02..91eae1fe 100644 --- a/demo/src/demoData/dataDefinitions.tsx +++ b/demo/src/demoData/dataDefinitions.tsx @@ -845,6 +845,7 @@ export const demoDataDefinitions: Record = { hideKey: true, customNodeProps: { components: { + // @ts-expect-error Ignore _ var a: ({ _, ...props }) =>
, }, }, diff --git a/demo/vite.config.ts b/demo/vite.config.ts index 344c3052..b9d7c94e 100644 --- a/demo/vite.config.ts +++ b/demo/vite.config.ts @@ -67,6 +67,7 @@ export default defineConfig({ vendor: ['react', 'react-dom', 'wouter', 'use-undo'], // JSON utilities json: ['json5', 'ajv'], + jsonEditReact: ['json-edit-react'], }, }, }, From 1981b124ddb6a2bd8c336fa19693d4b90554b334 Mon Sep 17 00:00:00 2001 From: Carl Smith <5456533+CarlosNZ@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:14:30 +1200 Subject: [PATCH 04/10] Lazy load JsonEditor --- demo/src/App.tsx | 440 ++++++++++++++++++++++++----------------------- 1 file changed, 224 insertions(+), 216 deletions(-) diff --git a/demo/src/App.tsx b/demo/src/App.tsx index 714be0e5..68b5be5e 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -2,7 +2,6 @@ import { useEffect, useRef, lazy, Suspense } from 'react' import { useSearch, useLocation } from 'wouter' import JSON5 from 'json5' import { - JsonEditor, Theme, FilterFunction, JsonData, @@ -40,8 +39,8 @@ import { NumberDecrementStepper, useToast, Tooltip, + Spinner, } from '@chakra-ui/react' -// import logo from './image/logo_400.png' import logoSVG from './image/logo.svg' import { ArrowBackIcon, ArrowForwardIcon, InfoIcon } from '@chakra-ui/icons' import { demoDataDefinitions } from './demoData' @@ -52,6 +51,7 @@ import { Loading } from '../../custom-component-library/components/_common/Loadi const CodeEditor = lazy(() => import('./CodeEditor')) const SourceIndicator = lazy(() => import('./SourceIndicator')) +const JsonEditor = lazy(() => import('json-edit-react').then((m) => ({ default: m.JsonEditor }))) interface AppState { rootName: string @@ -380,222 +380,230 @@ function App() { Demo - + +
} - onFocus={() => setIsSearchFocused(true)} - onBlur={() => setIsSearchFocused(false)} - bgColor={'#f6f6f6'} - borderColor="gainsboro" - borderRadius={50} - size="sm" - w={20} - value={searchText} - onChange={(e) => updateState({ searchText: e.target.value })} - position="absolute" - right={2} - top={2} - zIndex={100} - _focus={{ w: '45%' }} - transition={'width 0.3s'} - /> - void} - rootName={rootName} - theme={[theme, dataDefinition?.styles ?? {}, { container: { paddingTop: '1em' } }]} - indent={indent} - onUpdate={async (nodeData) => { - const demoOnUpdate = dataDefinition?.onUpdate ?? (() => undefined) - const result = await demoOnUpdate(nodeData, toast as (options: unknown) => void) - if (result) return result - else { - const { newData } = nodeData - if (selectedDataSet === 'editTheme') updateState({ theme: newData as Theme }) + > + { - const error = (dataDefinition.onError as OnErrorFunction)(errorData) - toast({ - title: 'ERROR 😢', - description: error as string, - status: 'error', - duration: 5000, - isClosable: true, - }) - } - : undefined - } - showErrorMessages={dataDefinition.showErrorMessages} - collapse={collapseLevel} - collapseAnimationTime={collapseTime} - showCollectionCount={ - showCount === 'Yes' ? true : showCount === 'When closed' ? 'when-closed' : false - } - enableClipboard={ - allowCopy - ? ({ stringValue, type, success, errorMessage }) => - success - ? toast({ - title: `${type === 'value' ? 'Value' : 'Path'} copied to clipboard:`, - description: truncate(String(stringValue)), - status: 'success', - duration: 5000, - isClosable: true, - }) - : toast({ - title: 'Problem copying to clipboard', - description: errorMessage, - status: 'error', - duration: 5000, - isClosable: true, - }) - : false - } - // viewOnly - restrictEdit={restrictEdit} - // restrictEdit={(nodeData) => !(typeof nodeData.value === 'string')} - restrictDelete={restrictDelete} - restrictAdd={restrictAdd} - restrictTypeSelection={dataDefinition?.restrictTypeSelection} - // restrictTypeSelection={[ - // 'string', - // 'number', - // 'boolean', - // 'null', - // { enum: 'Option', values: ['One', 'Two', 'Three'] }, - // { - // enum: 'Hobby', - // values: ['partying', 'building stuff', 'avenging', 'time travel'], - // matchPriority: 1, - // }, - // { - // enum: 'Other activities that could be quite long', - // values: ['changing', 'building stuff', 'avenging', 'money money money money'], - // matchPriority: 2, - // }, - // ]} - restrictDrag={false} - searchFilter={dataDefinition?.searchFilter} - searchText={searchText} - keySort={sortKeys} - // keySort={ - // sortKeys - // ? (a, b) => { - // const nameRev1 = String(a[0]).length - // const nameRev2 = String(b[0]).length - // if (nameRev1 < nameRev2) { - // return -1 - // } - // if (nameRev1 > nameRev2) { - // return 1 - // } - // return 0 - // } - // : false - // } - defaultValue={dataDefinition?.defaultValue ?? defaultNewValue} - newKeyOptions={dataDefinition?.newKeyOptions} - showArrayIndices={showIndices} - showStringQuotes={showStringQuotes} - minWidth={'min(500px, 95vw)'} - maxWidth="min(670px, 90vw)" - className="block-shadow" - stringTruncate={90} - customNodeDefinitions={customNodeDefinitions} - // customNodeDefinitions={[ - // { - // condition: ({ key }) => key === 'string', - // element: ({ nodeData, value, originalNode, originalNodeKey }) => ( - //
- // {originalNodeKey} - // {/* {nodeData.key} */} - // ICON:{' '} - // {originalNode} - //
- // ), - // hideKey: true, - // passOriginalNode: true, - // showOnEdit: true, - // }, - // ]} - customText={dataDefinition?.customTextDefinitions} - // icons={{ chevron: }} - // customButtons={[ - // { - // Element: () => ( - // - // - // - // - // ), - // onClick: (nodeData, e) => console.log(nodeData), - // }, - // ]} - onChange={dataDefinition?.onChange ?? undefined} - jsonParse={JSON5.parse} - // keyboardControls={{ - // cancel: 'Tab', - // confirm: { key: 'Enter', modifier: 'Meta' }, - // objectConfirm: { key: 'Enter', modifier: 'Shift' }, - // stringLineBreak: { key: 'Enter' }, - // stringConfirm: { key: 'Enter', modifier: 'Meta' }, - // clipboardModifier: ['Alt', 'Shift'], - // collapseModifier: 'Control', - // booleanConfirm: 'Enter', - // booleanToggle: 'r', - // }} - // insertAtBeginning="object" - // rootFontSize={20} - TextEditor={ - customTextEditor - ? (props) => ( - - - - } - > - - - ) - : undefined - } - // collapseClickZones={['property', 'header']} - // onEditEvent={(path) => { - // console.log(path) - // setIsEditing(path ? true : false) - // }} - // onCollapse={(input) => { - // const path = JSON.stringify(input.path) - // const newCollapseState = { ...collapseState.current, [path]: input } - // collapseState.current = newCollapseState - // localStorage.setItem('collapseState', JSON.stringify(newCollapseState)) - // }} - // externalTriggers={triggers} - // translations={{ - // EMPTY_STRING: 'Nah', - // }} - /> + onFocus={() => setIsSearchFocused(true)} + onBlur={() => setIsSearchFocused(false)} + bgColor={'#f6f6f6'} + borderColor="gainsboro" + borderRadius={50} + size="sm" + w={20} + value={searchText} + onChange={(e) => updateState({ searchText: e.target.value })} + position="absolute" + right={2} + top={2} + zIndex={100} + _focus={{ w: '45%' }} + transition={'width 0.3s'} + /> + void} + rootName={rootName} + theme={[theme, dataDefinition?.styles ?? {}, { container: { paddingTop: '1em' } }]} + indent={indent} + onUpdate={async (nodeData) => { + const demoOnUpdate = dataDefinition?.onUpdate ?? (() => undefined) + const result = await demoOnUpdate(nodeData, toast as (options: unknown) => void) + if (result) return result + else { + const { newData } = nodeData + if (selectedDataSet === 'editTheme') updateState({ theme: newData as Theme }) + } + }} + onEdit={dataDefinition?.onEdit ?? undefined} + onAdd={dataDefinition?.onAdd ?? undefined} + onError={ + dataDefinition.onError + ? (errorData) => { + const error = (dataDefinition.onError as OnErrorFunction)(errorData) + toast({ + title: 'ERROR 😢', + description: error as string, + status: 'error', + duration: 5000, + isClosable: true, + }) + } + : undefined + } + showErrorMessages={dataDefinition.showErrorMessages} + collapse={collapseLevel} + collapseAnimationTime={collapseTime} + showCollectionCount={ + showCount === 'Yes' ? true : showCount === 'When closed' ? 'when-closed' : false + } + enableClipboard={ + allowCopy + ? ({ stringValue, type, success, errorMessage }) => + success + ? toast({ + title: `${type === 'value' ? 'Value' : 'Path'} copied to clipboard:`, + description: truncate(String(stringValue)), + status: 'success', + duration: 5000, + isClosable: true, + }) + : toast({ + title: 'Problem copying to clipboard', + description: errorMessage, + status: 'error', + duration: 5000, + isClosable: true, + }) + : false + } + // viewOnly + restrictEdit={restrictEdit} + // restrictEdit={(nodeData) => !(typeof nodeData.value === 'string')} + restrictDelete={restrictDelete} + restrictAdd={restrictAdd} + restrictTypeSelection={dataDefinition?.restrictTypeSelection} + // restrictTypeSelection={[ + // 'string', + // 'number', + // 'boolean', + // 'null', + // { enum: 'Option', values: ['One', 'Two', 'Three'] }, + // { + // enum: 'Hobby', + // values: ['partying', 'building stuff', 'avenging', 'time travel'], + // matchPriority: 1, + // }, + // { + // enum: 'Other activities that could be quite long', + // values: ['changing', 'building stuff', 'avenging', 'money money money money'], + // matchPriority: 2, + // }, + // ]} + restrictDrag={false} + searchFilter={dataDefinition?.searchFilter} + searchText={searchText} + keySort={sortKeys} + // keySort={ + // sortKeys + // ? (a, b) => { + // const nameRev1 = String(a[0]).length + // const nameRev2 = String(b[0]).length + // if (nameRev1 < nameRev2) { + // return -1 + // } + // if (nameRev1 > nameRev2) { + // return 1 + // } + // return 0 + // } + // : false + // } + defaultValue={dataDefinition?.defaultValue ?? defaultNewValue} + newKeyOptions={dataDefinition?.newKeyOptions} + showArrayIndices={showIndices} + showStringQuotes={showStringQuotes} + minWidth={'min(500px, 95vw)'} + maxWidth="min(670px, 90vw)" + className="block-shadow" + stringTruncate={90} + customNodeDefinitions={customNodeDefinitions} + // customNodeDefinitions={[ + // { + // condition: ({ key }) => key === 'string', + // element: ({ nodeData, value, originalNode, originalNodeKey }) => ( + //
+ // {originalNodeKey} + // {/* {nodeData.key} */} + // ICON:{' '} + // {originalNode} + //
+ // ), + // hideKey: true, + // passOriginalNode: true, + // showOnEdit: true, + // }, + // ]} + customText={dataDefinition?.customTextDefinitions} + // icons={{ chevron: }} + // customButtons={[ + // { + // Element: () => ( + // + // + // + // + // ), + // onClick: (nodeData, e) => console.log(nodeData), + // }, + // ]} + onChange={dataDefinition?.onChange ?? undefined} + jsonParse={JSON5.parse} + // keyboardControls={{ + // cancel: 'Tab', + // confirm: { key: 'Enter', modifier: 'Meta' }, + // objectConfirm: { key: 'Enter', modifier: 'Shift' }, + // stringLineBreak: { key: 'Enter' }, + // stringConfirm: { key: 'Enter', modifier: 'Meta' }, + // clipboardModifier: ['Alt', 'Shift'], + // collapseModifier: 'Control', + // booleanConfirm: 'Enter', + // booleanToggle: 'r', + // }} + // insertAtBeginning="object" + // rootFontSize={20} + TextEditor={ + customTextEditor + ? (props) => ( + + + + } + > + + + ) + : undefined + } + // collapseClickZones={['property', 'header']} + // onEditEvent={(path) => { + // console.log(path) + // setIsEditing(path ? true : false) + // }} + // onCollapse={(input) => { + // const path = JSON.stringify(input.path) + // const newCollapseState = { ...collapseState.current, [path]: input } + // collapseState.current = newCollapseState + // localStorage.setItem('collapseState', JSON.stringify(newCollapseState)) + // }} + // externalTriggers={triggers} + // translations={{ + // EMPTY_STRING: 'Nah', + // }} + /> + {/*