Skip to content
Merged

Docs #10

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
332 changes: 329 additions & 3 deletions README.md

Large diffs are not rendered by default.

29 changes: 15 additions & 14 deletions demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons'
import demoData from './data'
import { useDatabase } from './useDatabase'
import './style.css'
import { FilterMethod } from './json-edit-react/src/types'
import { FilterFunction } from './json-edit-react/src/types'

function App() {
const [selectedData, setSelectedData] = useState('basic')
Expand All @@ -53,7 +53,7 @@ function App() {
const previousThemeName = useRef('') // Used when resetting after theme editing
const toast = useToast()

const { guestbook, updateGuestbook } = useDatabase()
const { liveData, updateLiveData } = useDatabase()

const [{ present: data }, { set: setData, reset, undo, redo, canUndo, canRedo }] = useUndo(
demoData[selectedData].data
Expand All @@ -63,10 +63,10 @@ function App() {
switch (selectedData) {
case 'editTheme':
return
case 'liveGuestbook':
setCollapseLevel(demoData.liveGuestbook.collapse)
if (!guestbook) reset({ 'Oops!': "We couldn't load this data, sorry " })
else reset(guestbook)
case 'liveData':
setCollapseLevel(demoData.liveData.collapse)
if (!liveData) reset({ 'Oops!': "We couldn't load this data, sorry " })
else reset(liveData)
return
default:
const newData = demoData[selectedData]
Expand All @@ -75,19 +75,19 @@ function App() {
}
}, [selectedData, reset])

const restrictEdit: FilterMethod | boolean = (() => {
const restrictEdit: FilterFunction | boolean = (() => {
const customRestrictor = demoData[selectedData]?.restrictEdit
if (customRestrictor) return (input) => !allowEdit || customRestrictor(input)
return !allowEdit
})()

const restrictDelete: FilterMethod | boolean = (() => {
const restrictDelete: FilterFunction | boolean = (() => {
const customRestrictor = demoData[selectedData]?.restrictDelete
if (customRestrictor) return (input) => !allowDelete || customRestrictor(input)
return !allowDelete
})()

const restrictAdd: FilterMethod | boolean = (() => {
const restrictAdd: FilterFunction | boolean = (() => {
const customRestrictor = demoData[selectedData]?.restrictAdd
if (customRestrictor) return (input) => !allowAdd || customRestrictor(input)
return !allowAdd
Expand Down Expand Up @@ -115,9 +115,9 @@ function App() {
case 'editTheme':
reset(themes[previousThemeName.current])
return
case 'liveGuestbook':
case 'liveData':
setIsSaving(true)
await updateGuestbook(data)
await updateLiveData(data)
setIsSaving(false)
toast({
title: 'Whoosh!',
Expand All @@ -126,7 +126,7 @@ function App() {
duration: 5000,
isClosable: true,
})
console.log(guestbook)
console.log(liveData)
reset(data)
return
default:
Expand Down Expand Up @@ -207,6 +207,7 @@ function App() {
showArrayIndices={showIndices}
maxWidth={650}
className="block-shadow"
stringTruncate={80}
/>
<VStack w="100%" align="flex-end" gap={4}>
<HStack w="100%" justify="space-between" mt={4}>
Expand Down Expand Up @@ -238,13 +239,13 @@ function App() {
</Text>
<Button
colorScheme="accentScheme"
leftIcon={selectedData === 'liveGuestbook' ? <AiOutlineCloudUpload /> : <BiReset />}
leftIcon={selectedData === 'liveData' ? <AiOutlineCloudUpload /> : <BiReset />}
variant="outline"
onClick={handleReset}
visibility={canUndo ? 'visible' : 'hidden'}
isLoading={isSaving}
>
{selectedData === 'liveGuestbook' ? 'Push to the cloud' : 'Reset'}
{selectedData === 'liveData' ? 'Push to the cloud' : 'Reset'}
</Button>
</HStack>
</VStack>
Expand Down
6 changes: 3 additions & 3 deletions demo/src/data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const data = {
</Text>
</Flex>
),
restrictEdit: ({ value }) => typeof value === 'object' && value !== null,
// restrictEdit: ({ value }) => typeof value === 'object' && value !== null,
restrictDelete: ({ value }) => typeof value === 'object' && value !== null,
restrictAdd: ({ value }) => !Array.isArray(value),
collapse: 1,
Expand Down Expand Up @@ -1768,8 +1768,8 @@ const data = {
'editor.formatOnSave': true,
},
},
liveGuestbook: {
name: '📖 Live Guestbook',
liveData: {
name: '📖 Live Data (from database)',
description: (
<Text>
Let's try a little experiment. You can save this JSON object to a live database, so feel
Expand Down
Binary file removed demo/src/image/logo_500.png
Binary file not shown.
8 changes: 4 additions & 4 deletions demo/src/useDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ const db = getFirestore(firebaseApp)

export const useDatabase = () => {
const [value, loading, error] = useDocument(
doc(getFirestore(firebaseApp), 'json-edit-react', 'guestbook')
doc(getFirestore(firebaseApp), 'json-edit-react', 'live_json_data')
)

const updateGuestbook = async (data) => {
await setDoc(doc(db, 'json-edit-react', 'guestbook'), data, { merge: true })
const updateLiveData = async (data) => {
await setDoc(doc(db, 'json-edit-react', 'live_json_data'), data, { merge: true })
}

return { guestbook: value?.data(), loading, error, updateGuestbook }
return { liveData: value?.data(), loading, error, updateLiveData }
}
Binary file added image/logo192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/theme_edit_after.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/theme_edit_before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "json-edit-react",
"version": "0.9.0",
"description": "React component for editing/viewing JSON/object data",
"description": "React component for editing or viewing JSON/object data",
"main": "build/index.cjs.js",
"module": "build/index.esm.js",
"types": "build/index.d.ts",
Expand All @@ -23,7 +23,6 @@
"react-dom": "^18.2.0"
},
"dependencies": {
"@react-hookz/web": "^23.0.0",
"just-clone": "^6.2.0",
"object-property-assigner": "^1.0.1",
"object-property-extractor": "^1.0.6",
Expand Down
8 changes: 4 additions & 4 deletions src/ButtonPanels.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import React, { useEffect, useState } from 'react'
import { Icon } from './Icons'
import { useTheme } from './theme'
import { TranslateMethod } from './localisation'
import { CollectionDataType, CollectionKey, CopyMethod, CopyType } from './types'
import { TranslateFunction } from './localisation'
import { CollectionDataType, CollectionKey, CopyFunction, CopyType } from './types'
import './style.css'

interface EditButtonProps {
startEdit?: () => void
handleDelete?: () => void
enableClipboard: boolean | CopyMethod
enableClipboard: boolean | CopyFunction
handleAdd?: (newKey: string) => void
type?: CollectionDataType
data: unknown
path: CollectionKey[]
name: CollectionKey
translate: TranslateMethod
translate: TranslateFunction
}

export const EditButtons: React.FC<EditButtonProps> = ({
Expand Down
8 changes: 4 additions & 4 deletions src/CollectionNode.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useState, useMemo } from 'react'
import { ValueNodeWrapper } from './ValueNodeWrapper'
import { EditButtons, InputButtons } from './ButtonPanels'
import { CollectionNodeProps, ERROR_DISPLAY_TIME, ErrorString } from './types'
Expand Down Expand Up @@ -108,9 +108,9 @@ export const CollectionNode: React.FC<CollectionNodeProps> = ({ data, path, name
setStringifiedValue(JSON.stringify(data, null, 2))
}

const canEdit = !restrictEditFilter(filterProps)
const canDelete = !restrictDeleteFilter(filterProps)
const canAdd = !restrictAddFilter(filterProps)
const canEdit = useMemo(() => !restrictEditFilter(filterProps), [filterProps])
const canDelete = useMemo(() => !restrictDeleteFilter(filterProps), [filterProps])
const canAdd = useMemo(() => !restrictAddFilter(filterProps), [filterProps])

const showLabel = showArrayIndices || !(typeof path.slice(-1)[0] === 'number')

Expand Down
37 changes: 16 additions & 21 deletions src/JsonEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import React, { useCallback, useEffect, useState } from 'react'
import assign from 'object-property-assigner'
import extract from 'object-property-extractor'
import clone from 'just-clone'
import { useWindowSize } from '@react-hookz/web'
import { CollectionNode, isCollection } from './CollectionNode'
import { CollectionData, JsonEditorProps, FilterMethod, OnChangeMethod } from './types'
import { CollectionData, JsonEditorProps, FilterFunction, OnChangeFunction } from './types'
import { useTheme, ThemeProvider } from './theme'
import { getTranslateMethod } from './localisation'
import { getTranslateFunction } from './localisation'
import './style.css'

const Editor: React.FC<JsonEditorProps> = ({
Expand All @@ -20,7 +19,6 @@ const Editor: React.FC<JsonEditorProps> = ({
enableClipboard = true,
theme = 'default',
icons,
className,
indent = 4,
collapse = false,
restrictEdit = false,
Expand All @@ -33,11 +31,12 @@ const Editor: React.FC<JsonEditorProps> = ({
maxWidth = 600,
stringTruncate = 250,
translations = {},
className,
}) => {
const [data, setData] = useState<object>(srcData)
const { styles, setTheme, setIcons } = useTheme()
const collapseFilter = useCallback(getFilterMethod(collapse), [collapse])
const translate = useCallback(getTranslateMethod(translations), [translations])
const collapseFilter = useCallback(getFilterFunction(collapse), [collapse])
const translate = useCallback(getTranslateFunction(translations), [translations])

useEffect(() => {
setData(srcData)
Expand All @@ -48,11 +47,7 @@ const Editor: React.FC<JsonEditorProps> = ({
if (icons) setIcons(icons)
}, [theme, icons])

const { width } = useWindowSize()
// So component can't overflow the current viewport
const maximumWidth = Math.min(maxWidth, width - 10)

const onEdit: OnChangeMethod = async (value, path) => {
const onEdit: OnChangeFunction = async (value, path) => {
const { currentData, newData, currentValue, newValue } = updateDataObject(
data,
path,
Expand All @@ -74,7 +69,7 @@ const Editor: React.FC<JsonEditorProps> = ({
} else setData(newData)
}

const onDelete: OnChangeMethod = async (value, path) => {
const onDelete: OnChangeFunction = async (value, path) => {
const { currentData, newData, currentValue, newValue } = updateDataObject(
data,
path,
Expand All @@ -96,7 +91,7 @@ const Editor: React.FC<JsonEditorProps> = ({
} else setData(newData)
}

const onAdd: OnChangeMethod = async (value, path) => {
const onAdd: OnChangeFunction = async (value, path) => {
const { currentData, newData, currentValue, newValue } = updateDataObject(
data,
path,
Expand All @@ -118,9 +113,9 @@ const Editor: React.FC<JsonEditorProps> = ({
} else setData(newData)
}

const restrictEditFilter = getFilterMethod(restrictEdit)
const restrictDeleteFilter = getFilterMethod(restrictDelete)
const restrictAddFilter = getFilterMethod(restrictAdd)
const restrictEditFilter = getFilterFunction(restrictEdit)
const restrictDeleteFilter = getFilterFunction(restrictDelete)
const restrictAddFilter = getFilterFunction(restrictAdd)

const otherProps = {
name: rootName,
Expand All @@ -145,7 +140,7 @@ const Editor: React.FC<JsonEditorProps> = ({
return (
<div
className={'jer-editor-container ' + className}
style={{ ...styles.container, minWidth, maxWidth: maximumWidth }}
style={{ ...styles.container, minWidth, maxWidth }}
>
{isCollection(data) && <CollectionNode data={data} path={[]} {...otherProps} />}
</div>
Expand Down Expand Up @@ -184,10 +179,10 @@ const updateDataObject = (
}
}

const getFilterMethod = (collapse: boolean | number | FilterMethod): FilterMethod => {
if (typeof collapse === 'boolean') return () => collapse
if (typeof collapse === 'number') return ({ level }) => level >= collapse
return collapse
const getFilterFunction = (propValue: boolean | number | FilterFunction): FilterFunction => {
if (typeof propValue === 'boolean') return () => propValue
if (typeof propValue === 'number') return ({ level }) => level >= propValue
return propValue
}

export default JsonEditor
8 changes: 4 additions & 4 deletions src/ValueNodeWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useState, useMemo } from 'react'
import {
StringValue,
NumberValue,
Expand Down Expand Up @@ -91,10 +91,10 @@ export const ValueNodeWrapper: React.FC<ValueNodeProps> = ({
})
}

const filterProps = { key: name, path, level: path.length, value: data, size: 1 }
const filterProps = { key: name, path, level: path.length, value: data, size: null }

const canEdit = !restrictEditFilter(filterProps)
const canDelete = !restrictDeleteFilter(filterProps)
const canEdit = useMemo(() => !restrictEditFilter(filterProps), [filterProps])
const canDelete = useMemo(() => !restrictDeleteFilter(filterProps), [filterProps])

const inputProps = {
value,
Expand Down
4 changes: 2 additions & 2 deletions src/localisation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const localisedStrings = {
}

export type LocalisedStrings = typeof localisedStrings
export type TranslateMethod = (key: keyof LocalisedStrings, count?: number) => string
export type TranslateFunction = (key: keyof LocalisedStrings, count?: number) => string

const translate = (
translations: Partial<LocalisedStrings>,
Expand All @@ -21,6 +21,6 @@ const translate = (
return count === undefined ? string : string?.replace('{{count}}', String(count))
}

export const getTranslateMethod =
export const getTranslateFunction =
(translations: Partial<LocalisedStrings>) => (key: keyof LocalisedStrings, count?: number) =>
translate(translations, key, count)
2 changes: 1 addition & 1 deletion src/theme/themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ export type CompiledStyles = Record<ThemeableElement, React.CSSProperties>

export type ThemeName = keyof typeof themes

// Value(s) passed to "setTheme" method
// Value(s) passed to "setTheme" function
export type ThemeInput =
| ThemeName
| Theme
Expand Down
Loading