Skip to content
Draft
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
19 changes: 13 additions & 6 deletions core/src/comps/KeyValues.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fragment, useRef } from 'react';
import { Fragment, useRef, useState } from 'react';
import { useStore } from '../store';
import { useExpandsStore } from '../store/Expands';
import { useShowToolsDispatch } from '../store/ShowTools';
Expand All @@ -11,6 +11,8 @@ import { useHighlight } from '../utils/useHighlight';
import { type SectionElementResult } from '../store/Section';
import { Copied } from '../comps/Copied';
import { useIdCompat } from '../comps/useIdCompat';
import { ValueExtraComp } from '../section/ValueExtra';
import { KeyValueItemContext } from '../editor/store';

interface KeyValuesProps<T extends object> extends SectionElementResult<T> {
expandKey?: string;
Expand Down Expand Up @@ -114,12 +116,17 @@ export const KeyValuesItem = <T extends object>(props: KeyValuesProps<T>) => {
onMouseEnter: () => dispatch({ [subkeyid]: true }),
onMouseLeave: () => dispatch({ [subkeyid]: false }),
};

const [editable, setEditable] = useState(false)
return (
<RowComp className="w-rjv-line" value={value} keyName={keyName} keys={keys} parentValue={parentValue} {...reset}>
<KayName keyName={keyName} value={value} keys={keys} parentValue={parentValue} />
<Value keyName={keyName!} value={value} keys={keys} />
<Copied keyName={keyName} value={value as object} keys={keys} parentValue={parentValue} expandKey={subkeyid} />
</RowComp>
<KeyValueItemContext.Provider value={{ editable, setEditable }}>
<RowComp className="w-rjv-line" value={value} keyName={keyName} keys={keys} parentValue={parentValue} {...reset}>
<KayName keyName={keyName} value={value} keys={keys} parentValue={parentValue} />
<Value keyName={keyName!} value={value} keys={keys} />
<ValueExtraComp keyName={keyName!} value={value} keys={keys} expandKey={subkeyid} />
<Copied keyName={keyName} value={value as object} keys={keys} parentValue={parentValue} expandKey={subkeyid} />
</RowComp>
</KeyValueItemContext.Provider>
);
};

Expand Down
2 changes: 1 addition & 1 deletion core/src/comps/NestedOpen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const NestedOpen = <T extends object>(props: NestedOpenProps<T>) => {
<EllipsisComp keyName={keyName!} value={value} isExpanded={isExpanded} />
<BracketsClose isVisiable={isExpanded || !showArrow} isBrackets={isArray || isMySet} {...compProps} />
<CountInfoComp value={value} keyName={keyName!} />
<CountInfoExtraComps value={value} keyName={keyName!} />
<CountInfoExtraComps value={value} keyName={keyName!} expandKey={expandKey} parentValue={parentValue} />
<Copied keyName={keyName!} value={value} expandKey={expandKey} parentValue={parentValue} keys={keys} />
</span>
);
Expand Down
50 changes: 50 additions & 0 deletions core/src/editor/CountInfoExtra.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { type SectionElementProps } from '../store/Section';
import { useStore } from './store';
import { AddIcon } from './icon/add';
import { DeleteIcon } from './icon/delete';
import { useShowToolsStore } from '../store/ShowTools';

export const CountInfoExtraRender: SectionElementProps['render'] = (
{ children, ...reset },
{ value, parentValue, keyName, expandKey },
) => {
const showTools = useShowToolsStore();
const isShowTools = expandKey && showTools[expandKey];
if (!isShowTools) return null;

// const { onEdit } = useStore()

const click = async (event: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
event.stopPropagation();
// const keyOrValue = 'AddKeyOrValue';
// const isArray = Array.isArray(value);
// const isAdd = isArray ? true : !(keyOrValue in value);
// const result = isArray ? [...value, keyOrValue] : { ...value, [keyOrValue]: undefined };
// if (onAdd && setValue) {
// const maybeAdd = await onAdd(keyOrValue, result as T, props.value, isAdd);
// if (maybeAdd) {
// setValue!(result as T);
// }
// }
};
const deleteHandle = async (event: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
event.stopPropagation();
// if (onDelete && (keyName || typeof keyName === 'number') && parentValue) {
// const maybeDelete = await onDelete(keyName, value, parentValue as T, { namespace });
// if (maybeDelete && setParentValue) {
// if (Array.isArray(parentValue)) {
// parentValue.splice(keyName as number, 1);
// setParentValue([...parentValue] as T);
// } else if (keyName in parentValue) {
// delete (parentValue as Record<string, any>)[keyName as string];
// setParentValue({ ...parentValue } as T);
// }
// }
// }
};
return <>
<AddIcon onClick={click} />
{parentValue && <DeleteIcon onClick={deleteHandle} />}
</>
};

40 changes: 40 additions & 0 deletions core/src/editor/ValueExtra.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { type SectionElementProps } from '../store/Section';
import { useKeyValueItem, useStore } from './store';
import { EditIcon } from './icon/edit';
import { DeleteIcon } from './icon/delete';
import { useShowToolsStore } from '../store/ShowTools';

export const ValueExtraRender: SectionElementProps['render'] = (
{ children, ...reset },
{ value, parentValue, keyName, expandKey },
) => {
const { onEdit } = useStore();
const { setEditable } = useKeyValueItem()

const showTools = useShowToolsStore();
const isShowTools = expandKey && showTools[expandKey];
if (!isShowTools) return null;

const click = (evn: React.MouseEvent<SVGElement, MouseEvent>) => {
evn.stopPropagation();
setEditable?.(true);
};

const deleteHandle = async (evn: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
evn.stopPropagation();
// if (data && keyName && keyName in data && setValue && onDelete) {
// const maybeDelete = await onDelete(keyName, value as T, parentValue as T, { namespace });
// if (maybeDelete) {
// delete (data as Record<string, any>)[keyName as string];
// setValue({ ...data } as T);
// }
// }
};
return <>
{/* {visible && editableValue && onEdit && <EditIcon onClick={click} />}
{visible && editableValue && onDelete && <DeleteIcon onClick={deleteHandle} />} */}
<EditIcon onClick={click} />
<DeleteIcon onClick={deleteHandle} />
</>;
};

19 changes: 19 additions & 0 deletions core/src/editor/icon/add.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CSSProperties } from 'react';

export interface AddIconProps extends React.SVGAttributes<SVGElement> {}
export const AddIcon = (props: AddIconProps) => {
const { style } = props
const defaultStyle: CSSProperties = {
verticalAlign: 'middle',
display: 'inline-block',
cursor: 'pointer',
marginLeft: 5,
height: '1em',
width: '1em',
}
return (
<svg viewBox="0 0 40 40" fill="var(--w-rjv-add-color, currentColor)" {...props} style={{ ...style, ...defaultStyle}}>
<path d="M28.3940997,0 L11.625773,0 C4.34215619,0 0,4.34 0,11.62 L0,28.36 C0,35.66 4.34215619,40 11.625773,40 L28.3740897,40 C35.6577066,40 39.9999316,35.66 39.9999316,28.38 L39.9999316,11.62 C40.0198727,4.34 35.6777165,0 28.3940997,0 Z M30.12022,21.8947368 L21.9056145,21.8947368 L21.9056145,30.1052632 C21.9056145,31.1410526 21.0462404,32 20.0099363,32 C18.9736323,32 18.1142582,31.1410526 18.1142582,30.1052632 L18.1142582,21.8947368 L9.89965272,21.8947368 C8.86334865,21.8947368 8.00397454,21.0357895 8.00397454,20 C8.00397454,18.9642105 8.86334865,18.1052632 9.89965272,18.1052632 L18.1142582,18.1052632 L18.1142582,9.89473684 C18.1142582,8.85894737 18.9736323,8 20.0099363,8 C21.0462404,8 21.9056145,8.85894737 21.9056145,9.89473684 L21.9056145,18.1052632 L30.12022,18.1052632 C31.156524,18.1052632 32.0158982,18.9642105 32.0158982,20 C32.0158982,21.0357895 31.156524,21.8947368 30.12022,21.8947368 Z" />
</svg>
);
}
19 changes: 19 additions & 0 deletions core/src/editor/icon/delete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CSSProperties } from 'react';

export interface DeleteIconProps extends React.SVGAttributes<SVGElement> {}
export const DeleteIcon = (props: DeleteIconProps) => {
const { style } = props
const defaultStyle: CSSProperties = {
verticalAlign: 'middle',
display: 'inline-block',
cursor: 'pointer',
marginLeft: 5,
height: '1em',
width: '1em',
}
return (
<svg viewBox="0 0 40 40" fill="var(--w-rjv-delete-color, #dc3545)" {...props} style={{ ...style, ...defaultStyle}}>
<path d="M28.3940997,0 L11.625773,0 C4.34215619,0 0,4.34 0,11.62 L0,28.36 C0,35.66 4.34215619,40 11.625773,40 L28.3740897,40 C35.6577066,40 39.9999316,35.66 39.9999316,28.38 L39.9999316,11.62 C40.0198727,4.34 35.6777165,0 28.3940997,0 Z M30.3428039,27.0681654 C31.2346195,27.9595381 31.2346195,29.4349136 30.3428039,30.3262862 C29.88152,30.7873411 29.2972271,31.0025 28.7129341,31.0025 C28.1286412,31.0025 27.5443483,30.7873411 27.0830644,30.3262862 L20.0100446,23.2567787 L12.9370249,30.3262862 C12.475741,30.7873411 11.891448,31.0025 11.3071551,31.0025 C10.7228621,31.0025 10.1385692,30.7873411 9.67728531,30.3262862 C8.78464325,29.4234847 8.78464325,27.9709669 9.67728531,27.0681654 L16.7503051,19.9986579 L9.67728531,12.9291504 C8.78464325,12.0263488 8.78464325,10.573831 9.67728531,9.67102951 C10.5691008,8.77965683 12.0452093,8.77965683 12.9370249,9.67102951 L20.0100446,16.740537 L27.0830644,9.67102951 C27.9748799,8.77965683 29.4509884,8.77965683 30.3428039,9.67102951 C31.2346195,10.5624022 31.2346195,12.0377777 30.3428039,12.9291504 L23.2697842,19.9986579 L30.3428039,27.0681654 L30.3428039,27.0681654 Z" />
</svg>
);
}
24 changes: 24 additions & 0 deletions core/src/editor/icon/edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CSSProperties } from 'react';

export interface EditIconProps extends React.SVGAttributes<SVGElement> {}
export const EditIcon = (props: EditIconProps) => {
const { style } = props
const defaultStyle: CSSProperties = {
verticalAlign: 'middle',
display: 'inline-block',
cursor: 'pointer',
marginLeft: 5,
height: '1em',
width: '1em',
}
return (
<svg
viewBox="0 0 26 26"
fill="var(--w-rjv-edit-color, currentColor)"
{...props}
style={{ ...style, ...defaultStyle}}
>
<path d="M18,0 L7.74666667,0 C2.89333333,0 0,2.89333333 0,7.74666667 L0,17.9066667 C0,22.7733333 2.89333333,26 7.74666667,26 L18,26 C22.8533333,26 26.0001422,22.7733333 26.0001422,17.92 L26.0001422,7.74666667 C26.0133333,2.89333333 22.8533333,0 18,0 Z M11.9333333,20.68 C11.5466667,21.0666667 10.8133333,21.44 10.28,21.52 L7,21.9866667 C6.88,22 6.76,22.0133333 6.64,22.0133333 C6.09333333,22.0133333 5.58666667,21.8266667 5.22666667,21.4666667 C4.78666667,21.0266667 4.6,20.3866667 4.70666667,19.68 L5.17333333,16.4 C5.25333333,15.8533333 5.61333333,15.1333333 6.01333333,14.7466667 L11.96,8.8 C12.1719034,9.3972484 12.4397602,9.97314063 12.76,10.52 C12.8933333,10.7466667 13.04,10.96 13.16,11.12 C13.3066667,11.3466667 13.48,11.56 13.5866667,11.68 C13.6533333,11.7733333 13.7066667,11.84 13.7333333,11.8666667 C14.0666667,12.2666667 14.4533333,12.64 14.7866667,12.92 C14.88,13.0133333 14.9333333,13.0666667 14.96,13.08 C15.16,13.24 15.36,13.4 15.5333333,13.52 C15.7466667,13.68 15.96,13.8266667 16.1866667,13.9466667 C16.4533333,14.1066667 16.7466667,14.2533333 17.04,14.4 C17.3466667,14.5333333 17.6266667,14.6533333 17.9066667,14.7466667 L11.9333333,20.68 L11.9333333,20.68 Z M20.4933333,12.12 L19.2666667,13.36 C19.1887174,13.4373865 19.0831716,13.4805643 18.9733333,13.4800055 C18.9333333,13.4800055 18.88,13.4800055 18.8533333,13.4666667 C16.1309823,12.6775025 14.0024975,10.5490177 13.2133333,7.82666667 C13.1733333,7.68 13.2133333,7.52 13.32,7.42666667 L14.56,6.18666667 C16.5866667,4.16 18.52,4.2 20.5066667,6.18666667 C21.52,7.2 22.0133333,8.17333333 22.0133333,9.18666667 C22,10.1466667 21.5066667,11.1066667 20.4933333,12.12 L20.4933333,12.12 Z" />
</svg>
)
}
44 changes: 41 additions & 3 deletions core/src/editor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { forwardRef } from 'react';
import { forwardRef, useRef } from 'react';
import JsonView, { type JsonViewProps } from '../';
import { KeyNameRender } from './KeyName';
import { Context, Dispatch, useStoreReducer } from './store';
import { CountInfoExtraRender } from './CountInfoExtra';
import { Context, Dispatch, useKeyValueItem, useStore, useStoreReducer } from './store';
import { ValueExtraRender } from './ValueExtra';

export interface JsonViewEditorProps<T extends object> extends Omit<JsonViewProps<T>, 'shortenTextAfterLength'> {
/**
Expand All @@ -25,8 +27,20 @@ const JsonViewEditor = forwardRef<HTMLDivElement, JsonViewEditorProps<object>>((
return (
<Context.Provider value={state}>
<Dispatch.Provider value={dispatch}>
<JsonView {...reset} shortenTextAfterLength={0} ref={ref}>
<JsonView {...reset} ref={ref}>
{editable && <JsonView.KeyName render={KeyNameRender} />}
{editable && <JsonView.String as={EditableSpan} />}
{editable && <JsonView.Null as={EditableSpan} />}
{editable && <JsonView.Undefined as={EditableSpan} />}
{editable && <JsonView.Nan as={EditableSpan} />}
{editable && <JsonView.Date as={EditableSpan} />}
{editable && <JsonView.Float as={EditableSpan} />}
{editable && <JsonView.Int as={EditableSpan} />}
{editable && <JsonView.Map as={EditableSpan} />}
{editable && <JsonView.Set as={EditableSpan} />}
{editable && <JsonView.Bigint as={EditableSpan} />}
{editable && <JsonView.CountInfoExtra render={CountInfoExtraRender} />}
{editable && <JsonView.ValueExtra render={ValueExtraRender} />}
{children}
</JsonView>
</Dispatch.Provider>
Expand All @@ -35,3 +49,27 @@ const JsonViewEditor = forwardRef<HTMLDivElement, JsonViewEditorProps<object>>((
});

export default JsonViewEditor;

const EditableSpan = ({ ...props }: any) => {
const ref = useRef<HTMLElement>(null)
const { editable, setEditable } = useKeyValueItem()
const { onEdit } = useStore()
if (editable) {
ref.current?.setAttribute("contentEditable", "true")
ref.current?.focus()
}

const onKeyDown = (evn: React.KeyboardEvent<HTMLSpanElement>) => {
if (evn.key === 'Enter') {
ref.current?.setAttribute('contentEditable', 'false');
setEditable(false)
}
};

const onBlur = (evn: React.FocusEvent<HTMLSpanElement, Element>) => {
ref.current?.setAttribute('contentEditable', 'false');
setEditable(false)
onEdit && onEdit({ value: evn.target.textContent, oldValue: props.children, keyName: "keyName" });
};
return <span {...props} ref={ref} onKeyDown={onKeyDown} onBlur={onBlur} >{props.children}</span>
}
5 changes: 5 additions & 0 deletions core/src/editor/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type Dispatch = React.Dispatch<InitialState>;

const initialState: InitialState = {};
export const Context = createContext<InitialState>(initialState);
export const KeyValueItemContext = createContext<{ editable: boolean, setEditable: (b: boolean) => void }>({ editable: false, setEditable: () => { } });

const reducer = (state: InitialState, action: InitialState) => ({
...state,
Expand All @@ -30,6 +31,10 @@ export const useStore = () => {
return useContext(Context);
};

export const useKeyValueItem = () => {
return useContext(KeyValueItemContext);
};

export function useStoreReducer(initialState: InitialState) {
return useReducer(reducer, initialState);
}
3 changes: 3 additions & 0 deletions core/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { CountInfoExtra } from './section/CountInfoExtra';
import { Ellipsis } from './section/Ellipsis';
import { KeyName } from './section/KeyName';
import { Row } from './section/Row';
import { ValueExtra } from './section/ValueExtra';

export * from './store';
export * from './store/Expands';
Expand Down Expand Up @@ -107,6 +108,7 @@ type JsonViewComponent = React.FC<React.PropsWithRef<JsonViewProps<object>>> & {
CountInfo: typeof CountInfo;
CountInfoExtra: typeof CountInfoExtra;
KeyName: typeof KeyName;
ValueExtra: typeof ValueExtra;
Row: typeof Row;
};

Expand Down Expand Up @@ -191,6 +193,7 @@ JsonView.Copied = Copied;
JsonView.CountInfo = CountInfo;
JsonView.CountInfoExtra = CountInfoExtra;
JsonView.KeyName = KeyName;
JsonView.ValueExtra = ValueExtra;
JsonView.Row = Row;

JsonView.displayName = 'JVR.JsonView';
Expand Down
6 changes: 4 additions & 2 deletions core/src/section/CountInfoExtra.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,22 @@ CountInfoExtra.displayName = 'JVR.CountInfoExtra';

export interface CountInfoExtraCompsProps<T extends object> {
value?: T;
parentValue?: T;
keyName: string | number;
expandKey?: string;
}

export const CountInfoExtraComps = <T extends object, K extends TagType>(
props: SectionElementProps<K> & CountInfoExtraCompsProps<T>,
) => {
const { value = {}, keyName, ...other } = props;
const { value = {}, parentValue, keyName, expandKey, ...other } = props;
const { CountInfoExtra: Comp = {} } = useSectionStore();
const { as, render, ...reset } = Comp;
if (!render && !reset.children) return null;
const Elm = as || 'span';
const isRender = render && typeof render === 'function';
const elmProps = { ...reset, ...other };
const child = isRender && render(elmProps as React.HTMLAttributes<K>, { value, keyName });
const child = isRender && render(elmProps as React.HTMLAttributes<K>, { value, keyName, parentValue, expandKey });
if (child) return child;
return <Elm {...elmProps} />;
};
Expand Down
40 changes: 40 additions & 0 deletions core/src/section/ValueExtra.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { type TagType } from '../store/Types';
import { type SectionElement, useSectionStore } from '../store/Section';
import { useSectionRender } from '../utils/useRender';
import { type SectionElementResult } from '../store/Section';

export const ValueExtra = <K extends TagType>(props: SectionElement<K>) => {
const { ValueExtra: Comp = {} } = useSectionStore();
useSectionRender(Comp, props, 'ValueExtra');
return null;
};

ValueExtra.displayName = 'JVR.ValueExtra';

export interface ValueExtraCompProps<T extends object> extends React.HTMLAttributes<HTMLDivElement>, SectionElementResult<T> {
}

export const ValueExtraComp = <T extends object>(props: React.PropsWithChildren<ValueExtraCompProps<T>>) => {
const { children, value, parentValue, keyName, keys, expandKey, ...other } = props;
const { ValueExtra: Comp = {} } = useSectionStore();
const { as, render, children: _, ...reset } = Comp;
const Elm = as || 'div';
const child =
render &&
typeof render === 'function' &&
render({ ...other, ...reset, children }, { value, keyName, parentValue, keys, expandKey });
if (child) return child;
return (
<Elm {...other} {...reset}>
{children}
</Elm>
);
};

ValueExtraComp.displayName = 'JVR.ValueExtraComp';






Loading