From 8992e1afa8f7d7771e5fbf6df4d548a737ea1afa Mon Sep 17 00:00:00 2001 From: Thomas Granbohm Date: Tue, 28 Jun 2022 16:07:27 +0200 Subject: [PATCH 1/6] Add base functionality for move action --- example/src/App.tsx | 19 +- .../src/components/Button/Button.module.css | 2 + example/src/components/Button/index.tsx | 8 +- .../src/components/CaptionedImage/index.tsx | 9 + example/src/components/Heading/index.tsx | 16 +- .../components/Paragraph/Paragraph.module.css | 4 + example/src/components/Paragraph/index.tsx | 7 + example/src/config.tsx | 10 + package-lock.json | 319 ++++-------------- package.json | 2 +- packages/djedi-json/src/Renderer.tsx | 2 + .../djedi-json/src/contexts/editcontext.tsx | 4 + packages/djedi-json/src/core/CMS/index.tsx | 7 + .../djedi-json/src/core/Editable/index.tsx | 17 +- packages/djedi-json/src/core/Tree/reducer.ts | 38 +++ packages/djedi-json/src/core/Tree/types.ts | 8 + 16 files changed, 200 insertions(+), 272 deletions(-) create mode 100644 example/src/components/Paragraph/Paragraph.module.css create mode 100644 example/src/components/Paragraph/index.tsx diff --git a/example/src/App.tsx b/example/src/App.tsx index 924822e..f839c16 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -23,8 +23,23 @@ const DUMMY_TREE: NodeTreeItem = { }, }, { - type: 'component/container', - content: {}, + type: 'component/heading', + content: { children: 'This' }, + }, + { + type: 'component/heading', + content: { children: 'heading is' }, + }, + { + type: 'component/heading', + content: { children: 'directly editable' }, + }, + { + type: 'component/paragraph', + content: { + children: + 'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Odio facilis vel consectetur dolore fugit enim nam ratione culpa numquam corporis eum debitis, incidunt sint saepe modi? Maxime explicabo cupiditate nobis.', + }, }, ], }, diff --git a/example/src/components/Button/Button.module.css b/example/src/components/Button/Button.module.css index 472086c..36160aa 100644 --- a/example/src/components/Button/Button.module.css +++ b/example/src/components/Button/Button.module.css @@ -3,6 +3,8 @@ color: white; border: none; border-radius: 4px; + + cursor: pointer; } .color-black { diff --git a/example/src/components/Button/index.tsx b/example/src/components/Button/index.tsx index 0a1aa7f..03939f9 100644 --- a/example/src/components/Button/index.tsx +++ b/example/src/components/Button/index.tsx @@ -3,13 +3,15 @@ import cx from 'classnames'; import styles from './Button.module.css'; -export interface ButtonProps extends React.HTMLProps { +export interface ButtonProps extends React.ButtonHTMLAttributes { color?: 'gray' | 'black'; } -const Button: React.FC = ({ children, color = 'gray' } = {}) => { +const Button: React.FC = ({ children, color = 'gray', ...props } = {}) => { return ( - + ); }; diff --git a/example/src/components/CaptionedImage/index.tsx b/example/src/components/CaptionedImage/index.tsx index 9d64493..e752f4a 100644 --- a/example/src/components/CaptionedImage/index.tsx +++ b/example/src/components/CaptionedImage/index.tsx @@ -1,6 +1,8 @@ import React from 'react'; import cx from 'classnames'; +import { useEdit } from 'djedi-json'; +import Button from '../Button'; import styles from './CaptionedImage.module.css'; export type CaptionedImageProps = { @@ -14,6 +16,10 @@ const CaptionedImage: React.FC = ({ image = 'https://source.unsplash.com/random/500x300', text = 'caption', }) => { + const { move, tree } = useEdit(); + + const onClick = (step: number) => move({ ...tree.content }, step); + return (
@@ -25,6 +31,9 @@ const CaptionedImage: React.FC = ({ })} > {text} + + +
); diff --git a/example/src/components/Heading/index.tsx b/example/src/components/Heading/index.tsx index 869cf05..fc335c7 100644 --- a/example/src/components/Heading/index.tsx +++ b/example/src/components/Heading/index.tsx @@ -1,5 +1,7 @@ import React from 'react'; +import { useEdit } from 'djedi-json'; +import Button from '../Button'; import styles from './Heading.module.css'; const Heading: React.FC<{ children: string; onChange?: (t: string) => void }> = ({ @@ -13,6 +15,10 @@ const Heading: React.FC<{ children: string; onChange?: (t: string) => void }> = }, [onChange] ); + const { move, tree } = useEdit(); + const { content } = tree; + + const onClick = React.useCallback((step: number) => move({ ...content }, step), [content]); React.useEffect(() => { ref.current?.addEventListener('keyup', handleKeyDown); @@ -20,9 +26,13 @@ const Heading: React.FC<{ children: string; onChange?: (t: string) => void }> = }, [handleKeyDown]); return ( -

- {children} -

+
+

+ {children} +

+ + +
); }; diff --git a/example/src/components/Paragraph/Paragraph.module.css b/example/src/components/Paragraph/Paragraph.module.css new file mode 100644 index 0000000..b5831e4 --- /dev/null +++ b/example/src/components/Paragraph/Paragraph.module.css @@ -0,0 +1,4 @@ +.root { + margin: 0 auto; + max-width: 80ch; +} diff --git a/example/src/components/Paragraph/index.tsx b/example/src/components/Paragraph/index.tsx new file mode 100644 index 0000000..66201f2 --- /dev/null +++ b/example/src/components/Paragraph/index.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +import styles from './Paragraph.module.css'; + +const Paragraph: React.FC = ({ children }) =>

{children}

; + +export default Paragraph; diff --git a/example/src/config.tsx b/example/src/config.tsx index 9cb2571..a82ed8b 100644 --- a/example/src/config.tsx +++ b/example/src/config.tsx @@ -5,6 +5,7 @@ import Grid from './components/Grid'; import Heading from './components/Heading'; import Hero from './components/Hero'; import Page from './components/Page'; +import Paragraph from './components/Paragraph'; import Unsplash, { TYPE_IDENTIFIER as UNSPLASHED_TYPE, UnsplashedImage } from './edits/Unsplashed'; const components: ComponentConfig[] = [ @@ -60,6 +61,15 @@ const components: ComponentConfig[] = [ children: CMSType.children({ self: false, allowed: ['component/caption-image'] }), }), }, + { + title: 'Paragraph', + Component: Paragraph, + type: 'component/paragraph', + editOnClick: true, + content: { + text: CMSType.string(), + }, + }, ]; const config = createConfig({ diff --git a/package-lock.json b/package-lock.json index f0054c7..453c152 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "devDependencies": { "@testing-library/jest-dom": "^5.5.0", "@testing-library/react": "^11.2.3", - "@types/jest": "^24.0.24", + "@types/jest": "^27.0.4", "@types/node": "^14.14.31", "@types/react": "^17.0.2", "@types/react-dom": "^17.0.1", @@ -3033,11 +3033,39 @@ } }, "node_modules/@types/jest": { - "version": "24.9.1", + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", "dev": true, - "license": "MIT", "dependencies": { - "jest-diff": "^24.3.0" + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, "node_modules/@types/json-schema": { @@ -6541,14 +6569,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "node_modules/diff-sequences": { - "version": "24.9.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -10831,149 +10851,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-diff": { - "version": "24.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-diff/node_modules/@jest/types": { - "version": "24.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-diff/node_modules/@types/istanbul-reports": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-diff/node_modules/@types/yargs": { - "version": "13.0.12", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-diff/node_modules/ansi-regex": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-diff/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/jest-diff/node_modules/jest-get-type": { - "version": "24.9.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "24.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "16.13.1", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/jest-docblock": { "version": "27.4.0", "dev": true, @@ -23441,7 +23318,7 @@ } }, "packages/djedi-json": { - "version": "0.5.6", + "version": "0.5.7", "license": "ISC", "dependencies": { "classnames": "^2.3.1", @@ -26264,10 +26141,32 @@ } }, "@types/jest": { - "version": "24.9.1", + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", "dev": true, "requires": { - "jest-diff": "^24.3.0" + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + } } }, "@types/json-schema": { @@ -28918,10 +28817,6 @@ } } }, - "diff-sequences": { - "version": "24.9.0", - "dev": true - }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -32516,106 +32411,6 @@ } } }, - "jest-diff": { - "version": "24.9.0", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/istanbul-reports": { - "version": "1.1.2", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "@types/yargs": { - "version": "13.0.12", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "4.1.0", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "dev": true - }, - "jest-get-type": { - "version": "24.9.0", - "dev": true - }, - "pretty-format": { - "version": "24.9.0", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } - }, - "react-is": { - "version": "16.13.1", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "jest-docblock": { "version": "27.4.0", "dev": true, diff --git a/package.json b/package.json index c9aed39..46cdd67 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "devDependencies": { "@testing-library/jest-dom": "^5.5.0", "@testing-library/react": "^11.2.3", - "@types/jest": "^24.0.24", + "@types/jest": "^27.0.4", "@types/node": "^14.14.31", "@types/react": "^17.0.2", "@types/react-dom": "^17.0.1", diff --git a/packages/djedi-json/src/Renderer.tsx b/packages/djedi-json/src/Renderer.tsx index bd19ffa..9515c52 100644 --- a/packages/djedi-json/src/Renderer.tsx +++ b/packages/djedi-json/src/Renderer.tsx @@ -21,6 +21,8 @@ const Renderer: React.FC = ({ config, tree }) => { const { children, ...props } = tree.content; + console.log('tree:', tree); + return ( {Array.isArray(children) diff --git a/packages/djedi-json/src/contexts/editcontext.tsx b/packages/djedi-json/src/contexts/editcontext.tsx index 1751ff3..d8181f6 100644 --- a/packages/djedi-json/src/contexts/editcontext.tsx +++ b/packages/djedi-json/src/contexts/editcontext.tsx @@ -5,6 +5,7 @@ export type EditContextType = { path: string[]; tree: NodeTreeItem; append: (t: string, o?: Record) => void; + move: (p: NodeContentType, d: number) => void; remove: () => void; patch: (p: NodeContentType) => void; setEdit: (v: boolean) => void; @@ -27,6 +28,9 @@ const EditContext = React.createContext({ patch: () => { // not empty }, + move: () => { + // not empty + }, path: [], }); diff --git a/packages/djedi-json/src/core/CMS/index.tsx b/packages/djedi-json/src/core/CMS/index.tsx index 8b56568..8f602c9 100644 --- a/packages/djedi-json/src/core/CMS/index.tsx +++ b/packages/djedi-json/src/core/CMS/index.tsx @@ -30,6 +30,13 @@ const CMS: React.FC = ({ [passedTree] ); + React.useEffect(() => { + console.log('Tree updated:', tree); + }, [tree]); + React.useEffect(() => { + console.log('PassedTree updated:', passedTree); + }, [passedTree]); + // keep config in sync React.useEffect(() => setConfig(passedConfig), [passedConfig]); diff --git a/packages/djedi-json/src/core/Editable/index.tsx b/packages/djedi-json/src/core/Editable/index.tsx index 5f9e376..0fb6d3f 100644 --- a/packages/djedi-json/src/core/Editable/index.tsx +++ b/packages/djedi-json/src/core/Editable/index.tsx @@ -9,6 +9,7 @@ import Append from '../Append'; import EditGroup from '../EditGroup'; import { createEmpty } from '../Node'; import styles from './Editable.module.css'; +import { TreeReducerAction } from '../Tree/types'; /** * Editable, wraps the child component with some tooling for talking to the admin. @@ -62,6 +63,18 @@ const Editable: React.FC<{ [path, tree, setTree] ); + const move = React.useCallback( + (_content: NodeContentType, direction: number) => { + setTree({ + path, + payload: { ...tree, content: _content }, + type: 'move', + direction, + }); + }, + [path, setTree, tree] + ); + const remove = React.useCallback(() => setTree({ type: 'delete', path }), [path, setTree]); const toggleOpen = (bool: boolean) => { @@ -83,7 +96,9 @@ const Editable: React.FC<{ }; return ( - + {isomorphic ? ( ) : ( diff --git a/packages/djedi-json/src/core/Tree/reducer.ts b/packages/djedi-json/src/core/Tree/reducer.ts index 5fbfce3..f4bd53f 100644 --- a/packages/djedi-json/src/core/Tree/reducer.ts +++ b/packages/djedi-json/src/core/Tree/reducer.ts @@ -5,6 +5,7 @@ import { TreeReducerAction } from './types'; import { cleanTree } from './utils'; export const reducer = (state: NodeTreeItem, action: TreeReducerAction) => { + console.log("Reducer ran with action '%s'", action.type); switch (action.type) { case 'replace': return action.payload; @@ -41,6 +42,43 @@ export const reducer = (state: NodeTreeItem, action: TreeReducerAction) => { return cleanTree(nstate); } + case 'move': { + const nstate = { ...state }; + + if (Array.isArray(action.path)) { + const path = action.path.slice(); + + const starting_index = parseInt(path.pop()); + if (!Number.isNaN(starting_index)) { + debugger; + console.debug('%d is valid number', starting_index); + // // Calculate the desired index + // const desired_index = Math.min( + // Math.max(starting_index + action.direction, 0), + // get(nstate, path, []).length + // ); + // const raw_children: [] = get(nstate, path, []); + // // Remove the element we want to move + // const children = raw_children + // .slice(0, starting_index) + // .concat(raw_children.slice(starting_index + 1)); + // // Get every child before and after the desired index to make the insert + // const before = children.slice(0, desired_index); + // const after = children.slice(desired_index); + // const to_move = get(nstate, action.path, []); + // // console.trace(); + // // Create the new children + // const new_children = [...before, to_move, ...after]; + // // Set the new children + // set(nstate, path, [...new_children]); + } + } + + console.info('Running move.'); + + return nstate; + } + default: throw new Error('Invalid action'); } diff --git a/packages/djedi-json/src/core/Tree/types.ts b/packages/djedi-json/src/core/Tree/types.ts index 358af38..fae9462 100644 --- a/packages/djedi-json/src/core/Tree/types.ts +++ b/packages/djedi-json/src/core/Tree/types.ts @@ -24,7 +24,15 @@ export type DeleteAction = { path: string[] | string; }; +export type MoveAction = { + type: 'move'; + path: string[] | string; + payload: NodeTreeItem; + direction: number; +}; + export type TreeReducerAction = + | MoveAction | ReplaceAction | EmptyAction | PatchAction From 6e35ddc4b860ea48a352f5d63f969f8fa8b3efa8 Mon Sep 17 00:00:00 2001 From: akeamc Date: Wed, 6 Jul 2022 17:19:10 +0200 Subject: [PATCH 2/6] Add basic move functionality --- example/public/index.html | 5 +- .../src/components/CaptionedImage/index.tsx | 9 - example/src/components/Heading/index.tsx | 8 - packages/djedi-json/package-lock.json | 5689 +---------------- .../djedi-json/src/contexts/editcontext.tsx | 4 - .../djedi-json/src/core/Editable/index.tsx | 56 +- .../src/core/Renderer/EditorTree.tsx | 9 +- packages/djedi-json/src/core/Tree/reducer.ts | 50 +- packages/djedi-json/src/core/Tree/types.ts | 3 +- 9 files changed, 370 insertions(+), 5463 deletions(-) diff --git a/example/public/index.html b/example/public/index.html index aa069f2..6a9f8c2 100644 --- a/example/public/index.html +++ b/example/public/index.html @@ -5,10 +5,7 @@ - +