Skip to content

Commit

Permalink
fix: display: contents on Editable path wrapper, React pref (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
motform authored Jan 29, 2024
1 parent f7243ff commit a96b6de
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 98 deletions.
6 changes: 2 additions & 4 deletions packages/djedi-json/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
{
"name": "djedi-json",
"version": "0.6.0",
"version": "0.6.1",
"main": "build/index.js",
"module": "build/index.esm.js",
"types": "build/index.d.ts",
"files": [
"build"
],
"files": ["build"],
"scripts": {
"start": "rollup -c -w",
"build": "rollup -c",
Expand Down
3 changes: 2 additions & 1 deletion packages/djedi-json/src/CMSType/edits/Children/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ChildrenProps } from './type';

export const TYPE_IDENTIFIER = 'input/children';

export const type = (settings: ChildrenProps = {}) => {
return {
append: true,
...(settings || {}),
...settings,
type: TYPE_IDENTIFIER,
};
};
3 changes: 2 additions & 1 deletion packages/djedi-json/src/CMSType/edits/Select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { CMSEditProps } from '../../types';
import { SelectProps } from './type';

export const TYPE_IDENTIFIER = 'input/select';

export const type = (settings: SelectProps) => {
return {
nullable: true,
multiple: false,
...(settings || {}),
...settings,
type: TYPE_IDENTIFIER,
};
};
Expand Down
12 changes: 8 additions & 4 deletions packages/djedi-json/src/CMSType/edits/String/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const TYPE_IDENTIFIER = 'input/string';

export const type = (settings: Partial<StringProps> = {}) => {
return {
...(settings || {}),
...settings,
type: TYPE_IDENTIFIER,
};
};
Expand All @@ -19,18 +19,22 @@ const String: React.FC<StringProps & CMSEditProps<string>> = ({
onChange,
settings,
}) => {
const handleOnChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
onChange(e.target.value);
}, []);

return (
<label className={inputStyles.label}>
<span className={inputStyles.labelText}>{label}</span>
<input
{...(settings || {})}
{...settings}
value={value}
className={inputStyles.input}
type="text"
onChange={e => onChange(e.target.value)}
onChange={handleOnChange}
/>
</label>
);
};

export default String;
export default React.memo(String);
9 changes: 3 additions & 6 deletions packages/djedi-json/src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@ export interface ModalProps
onClose?: () => void;
}
const Modal: React.FC<ModalProps> = ({ children, onClose, className, ...props }) => {
const handleOnClick = React.useCallback(() => onClose?.(), [onClose]);

return (
<>
<style>{`body{overflow: "hidden";}`}</style>
<div
className={styles.backdrop}
onClick={() => {
onClose && onClose();
}}
>
<div className={styles.backdrop} onClick={handleOnClick}>
<div className={cx(styles.root, className)} {...props} onClick={e => e.stopPropagation()}>
{children}
</div>
Expand Down
33 changes: 20 additions & 13 deletions packages/djedi-json/src/core/Append/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,36 @@ const Append: React.FC<AppendProps> = ({ onClick, config }) => {
const ref = React.useRef<HTMLButtonElement>(null);
const { config: CMSConfig } = useCMS();

const possibleChildren = filterChildren(CMSConfig.components, config);
const possibleChildren = React.useMemo(
() => filterChildren(CMSConfig.components, config),
[config, CMSConfig.components]
);

const handleClick = (type: string) => {
const handleClick = React.useCallback((type: string) => {
setOpen(false);
onClick(type);
};
}, []);

const handleIconClick = React.useCallback(() => {
possibleChildren.length === 1 ? handleClick(possibleChildren[0].type) : setOpen(v => !v);
}, [possibleChildren]);

const handleChildClick = React.useCallback(
(type: string) => () => handleClick(type),
[handleClick]
);

return (
<div className={styles.wrapper}>
<button
className={styles.button}
onClick={() =>
possibleChildren.length === 1 ? handleClick(possibleChildren[0].type) : setOpen(v => !v)
}
ref={ref}
>
<button className={styles.button} onClick={handleIconClick} ref={ref}>
<AddIcon />
</button>

{open && (
<div className={styles.chooser}>
{possibleChildren.map(s => (
<button onClick={() => handleClick(s.type)} key={s.type}>
{s.icon || s.title.substring(0, 1)}
<button onClick={handleChildClick(s.type)} key={s.type}>
{s.icon ?? s.title.substring(0, 1)}
</button>
))}
</div>
Expand All @@ -66,4 +73,4 @@ const Append: React.FC<AppendProps> = ({ onClick, config }) => {
);
};

export default Append;
export default React.memo(Append);
6 changes: 3 additions & 3 deletions packages/djedi-json/src/core/CMS/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ const CMS: React.FC<CMSProps> = ({
// Keep the config in sync.
React.useEffect(() => setConfig(passedConfig), [passedConfig]);

return (
<CMSContext.Provider value={{ config, dirty, tree, setTree }}>{children}</CMSContext.Provider>
);
const value = React.useMemo(() => ({ config, dirty, tree, setTree }), [config, dirty, tree]);

return <CMSContext.Provider value={value}>{children}</CMSContext.Provider>;
};

export default CMS;
34 changes: 22 additions & 12 deletions packages/djedi-json/src/core/EditGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,47 @@ export type EditGroupProps = {
const EditGroup: React.FC<EditGroupProps> = ({ content }) => {
const { config } = useCMS();
const { patch, editing, tree, setEdit } = useEdit();
const c: ComponentConfig | undefined = config.components.find(t => t.type === tree.type);

const handleChange = (k: string) => (v: any) => {
patch({ ...(tree?.content || {}), [k]: v });
};
const c: ComponentConfig | undefined = React.useMemo(
() => config.components.find(t => t.type === tree.type),
[config, tree]
);

const handleChange = React.useCallback(
(k: string) => (v: any) => {
patch({ ...tree?.content, [k]: v });
},
[tree, patch]
);

const handleSetEdit = React.useCallback((edit: boolean) => () => setEdit(edit), [setEdit]);

return editing ? (
<Modal onClose={() => setEdit(false)}>
<Modal onClose={handleSetEdit(false)}>
<div className={styles.separate}>
<h2 className={styles.title}>{c?.title || ''}</h2>
<p>{c?.description || ''}</p>
<h2 className={styles.title}>{c?.title ?? ''}</h2>
<p>{c?.description ?? ''}</p>

{Object.entries(content).map(([k, editConfig]) => {
// for now; opt out of displaying children as a prop here.
if (k === 'children') {
return null;
}
if (k === 'children') return null;

const { type, ...editProps } = editConfig;
const { Component } = config.edit[type];

return (
<section className={styles.layout} key={k}>
<Component
label={k}
value={tree?.content[k] || undefined}
value={tree?.content[k] ?? undefined}
{...editProps}
onChange={handleChange(k)}
/>
</section>
);
})}
<Button onClick={() => setEdit(false)} className={styles.close}>

<Button onClick={handleSetEdit(false)} className={styles.close}>
<CloseSVG width="24px" fill="currentColor" />
</Button>
</div>
Expand Down
4 changes: 4 additions & 0 deletions packages/djedi-json/src/core/Editable/Editable.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@
.clickable {
cursor: pointer;
}

.path {
display: contents;
}
100 changes: 48 additions & 52 deletions packages/djedi-json/src/core/Editable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,37 @@ const Editable: React.FC<{

const remove = React.useCallback(() => setTree({ type: 'delete', path }), [path, setTree]);

const handleRemove = React.useCallback(() => remove(), [remove]);

const toggleOpen = React.useCallback((bool: boolean) => setOver(bool), [setOver]);

const handleOpen = React.useCallback(
(open: boolean) => (e: React.MouseEvent) => {
e.stopPropagation();
toggleOpen(open);
},
[]
);

const handleEdit = React.useCallback((e: React.MouseEvent) => {
e.stopPropagation();
setEdit(v => !v);
}, []);

const handleSetEdit = React.useCallback(() => {
if (config.editOnClick) {
return () => setEdit(true);
}
}, [config.editOnClick]);

const handleShift = React.useCallback(
(by: -1 | 1) => (e: React.MouseEvent) => {
e.stopPropagation();
shift(by);
},
[shift]
);

const componentProps = React.useMemo(() => {
return {
...tree?.content,
Expand All @@ -142,82 +171,49 @@ const Editable: React.FC<{
};
}, [tree, childrenConfig, append, config, children]);

const contextValue = React.useMemo(
// prettier-ignore
() => ({ editing, tree, setEdit, patch, remove, path, config, parentType, ref, append, insert, shift, move }),
// prettier-ignore
[editing, tree, setEdit, patch, remove, path, config, parentType, ref, append, insert, shift, move]
);

return (
<EditContext.Provider
value={{
editing,
tree,
setEdit,
patch,
remove,
path,
config,
parentType,
ref,
append,
insert,
shift,
move,
}}
>
<div data-path={treePath}>
<EditContext.Provider value={contextValue}>
<div className={styles.path} data-path={treePath}>
{isomorphic ? (
<Component key={tree.__ref} onChange={patch} {...componentProps} />
) : (
<>
<span
ref={ref}
onClick={config.editOnClick ? () => setEdit(true) : undefined}
onClick={handleSetEdit()}
className={cx(styles.root, { [styles.clickable]: config.editOnClick })}
onMouseEnter={e => {
e.stopPropagation();
toggleOpen(true);
}}
onMouseLeave={e => {
e.stopPropagation();
toggleOpen(false);
}}
onMouseEnter={handleOpen(true)}
onMouseLeave={handleOpen(false)}
>
<Component {...componentProps} />

{Boolean(over && (config.removable || config.editable || config.movable)) && (
<span className={styles.toolbar}>
{config.editable && (
<button
onClick={e => {
e.stopPropagation();
setEdit(v => !v);
}}
>
<button onClick={handleEdit}>
<EditSVG fill="currentColor" />
</button>
)}

{config.removable && (
<button
onClick={e => {
e.stopPropagation();
remove();
}}
>
<button onClick={handleRemove}>
<DeleteSVG fill="currentColor" />
</button>
)}

{config.movable && (
<>
<button
onClick={e => {
e.stopPropagation();
shift(-1);
}}
>
<button onClick={handleShift(-1)}>
<UpSVG fill="currentColor" />
</button>
<button
onClick={e => {
e.stopPropagation();
shift(1);
}}
>
<button onClick={handleShift(1)}>
<DownSVG fill="currentColor" />
</button>
</>
Expand All @@ -233,4 +229,4 @@ const Editable: React.FC<{
);
};

export default Editable;
export default React.memo(Editable);
5 changes: 3 additions & 2 deletions packages/djedi-json/src/core/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const validateConfig = (config: Config) => {
console.log(`%c✅ ${cc.type}:${k} validated as ${type}`, 'color: darkgreen');
return true;
}

//eslint-disable-next-line no-console
console.log(`%c❌ edit type missing for ${cc.type}`, 'color: red');
return false;
Expand All @@ -30,11 +31,11 @@ export const createConfig = (passedConfig: Partial<Config>): Config => {
const edit = {
...DEFAULT_EDIT_MAP,
// overwrite the defaults with the user supplied ones, in case something is needed
...(passedConfig.edit || {}),
...passedConfig.edit,
};
const config = {
...passedConfig,
components: [...(passedConfig?.components || [])].map(createNodeConfig),
components: [...(passedConfig?.components ?? [])].map(createNodeConfig),
edit: edit,
};

Expand Down

0 comments on commit a96b6de

Please sign in to comment.