From d76f40cf7d84cd96fcc59768855412a50cdf49d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Mon, 22 Feb 2021 19:13:37 +0100 Subject: [PATCH] Rename events, added mainmenu --- packages/core/lib/blocks.ts | 6 +- packages/core/lib/components/Block.tsx | 38 +++++----- .../core/lib/components/BlockOrdering.tsx | 12 +-- packages/core/lib/components/BlockPicker.tsx | 2 +- packages/core/lib/components/ContextMenu.scss | 0 packages/core/lib/components/ContextMenu.tsx | 74 +++++++++++++++++++ packages/core/lib/components/MainMenu.scss | 2 + packages/core/lib/components/MainMenu.tsx | 34 +++++++++ packages/core/lib/components/Modal.tsx | 6 +- .../core/lib/components/Schlechtenburg.tsx | 10 ++- .../core/lib/components/TreeBlockSelect.scss | 2 + .../core/lib/components/TreeBlockSelect.tsx | 38 ++++++++++ packages/image/lib/edit.tsx | 8 +- packages/layout/lib/edit.tsx | 48 +++++++----- packages/paragraph/lib/edit.tsx | 43 ++++++++--- src/App.tsx | 9 +-- 16 files changed, 259 insertions(+), 73 deletions(-) create mode 100644 packages/core/lib/components/ContextMenu.scss create mode 100644 packages/core/lib/components/ContextMenu.tsx create mode 100644 packages/core/lib/components/MainMenu.scss create mode 100644 packages/core/lib/components/MainMenu.tsx create mode 100644 packages/core/lib/components/TreeBlockSelect.scss create mode 100644 packages/core/lib/components/TreeBlockSelect.tsx diff --git a/packages/core/lib/blocks.ts b/packages/core/lib/blocks.ts index 7959327..c25c949 100644 --- a/packages/core/lib/blocks.ts +++ b/packages/core/lib/blocks.ts @@ -11,12 +11,12 @@ export interface BlockLibraryDefinition { [name: string]: BlockDefinition; } -export interface BlockProps { +export interface BlockProps { blockId: string; - data: T; + data: any; } -export interface Block extends BlockProps { +export interface Block extends BlockProps { name: string; } diff --git a/packages/core/lib/components/Block.tsx b/packages/core/lib/components/Block.tsx index 71c72c2..2e62b01 100644 --- a/packages/core/lib/components/Block.tsx +++ b/packages/core/lib/components/Block.tsx @@ -19,12 +19,14 @@ import './Block.scss'; interface BlockProps { block: Block; - eventUpdate: (b?: Block) => void; - eventPrependBlock: (b?: Block) => void; - eventAppendBlock: (b?: Block) => void; - eventRemoveBlock: () => void; - eventMoveUp: () => void; - eventMoveDown: () => void; + onUpdate: (b?: Block) => void; + onPrependBlock: (b?: Block) => void; + onAppendBlock: (b?: Block) => void; + onRemoveSelf: () => void; + onMoveBackward: () => void; + onMoveForward: () => void; + onActivateNext: () => void; + onActivatePrevious: () => void; sortable: string; } @@ -40,12 +42,12 @@ export const SbBlock = defineComponent({ type: String, default: null, }, - eventUpdate: { type: Function, default: () => {} }, - eventPrependBlock: { type: Function, default: () => {} }, - eventAppendBlock: { type: Function, default: () => {} }, - eventRemoveBlock: { type: Function, default: () => {} }, - eventMoveUp: { type: Function, default: () => {} }, - eventMoveDown: { type: Function, default: () => {} }, + onUpdate: { type: Function, default: () => {} }, + onPrependBlock: { type: Function, default: () => {} }, + onAppendBlock: { type: Function, default: () => {} }, + onRemoveSelf: { type: Function, default: () => {} }, + onMoveBackward: { type: Function, default: () => {} }, + onMoveForward: { type: Function, default: () => {} }, }, setup(props: BlockProps, context) { @@ -61,7 +63,7 @@ export const SbBlock = defineComponent({ watch(() => props.block.data, triggerSizeCalculation); const onChildUpdate = (updated: {[key: string]: any}) => { - props.eventUpdate({ + props.onUpdate({ ...props.block, data: { ...props.block.data, @@ -99,10 +101,12 @@ export const SbBlock = defineComponent({ { $event.stopPropagation(); activate(); diff --git a/packages/core/lib/components/BlockOrdering.tsx b/packages/core/lib/components/BlockOrdering.tsx index 260e561..08cce62 100644 --- a/packages/core/lib/components/BlockOrdering.tsx +++ b/packages/core/lib/components/BlockOrdering.tsx @@ -19,9 +19,9 @@ export const SbBlockOrdering = defineComponent({ type: String, default: null, }, - eventRemoveBlock: { type: Function, default: () => {} }, - eventMoveUp: { type: Function, default: () => {} }, - eventMoveDown: { type: Function, default: () => {} }, + onRemove: { type: Function, default: () => {} }, + onMoveUp: { type: Function, default: () => {} }, + onMoveDown: { type: Function, default: () => {} }, }, setup(props) { @@ -55,9 +55,9 @@ export const SbBlockOrdering = defineComponent({ style={styles} onClick={($event: MouseEvent) => $event.stopPropagation()} > - {props.sortable === 'vertical' ? '↑' : '←'} - x - {props.sortable === 'vertical' ? '↓' : '→'} + {props.sortable === 'vertical' ? '↑' : '←'} + x + {props.sortable === 'vertical' ? '↓' : '→'} ); }, diff --git a/packages/core/lib/components/BlockPicker.tsx b/packages/core/lib/components/BlockPicker.tsx index ac6cd61..f73b563 100644 --- a/packages/core/lib/components/BlockPicker.tsx +++ b/packages/core/lib/components/BlockPicker.tsx @@ -46,7 +46,7 @@ export const SbBlockPicker = defineComponent({ $event.stopPropagation()} - eventClose={() => { + onClose={() => { open.value = false; }} > diff --git a/packages/core/lib/components/ContextMenu.scss b/packages/core/lib/components/ContextMenu.scss new file mode 100644 index 0000000..e69de29 diff --git a/packages/core/lib/components/ContextMenu.tsx b/packages/core/lib/components/ContextMenu.tsx new file mode 100644 index 0000000..b909200 --- /dev/null +++ b/packages/core/lib/components/ContextMenu.tsx @@ -0,0 +1,74 @@ +import { + watch, + defineComponent, + ref, +} from 'vue'; + +import { SbButton } from './Button'; + +import './ContextMenu.scss'; + +interface ContextMenuProps { + onClose: () => void; + onOpen: () => void; +} + +export const SbContextMenu = defineComponent({ + name: 'sb-context-menu', + + props: { + onClose: { type: Function, default: () => {} }, + onOpen: { type: Function, default: () => {} }, + }, + + setup(props: ContextMenuProps, context) { + const opened = ref(false); + const close = () => { opened.value = false; } + const closeOnEscape = ($event: KeyboardEvent) => { + if ($event.key === 'Escape') { + close(); + } + }; + const open = () => { + opened.value = true; + document.addEventListener('click', close); + document.addEventListener('keypress', closeOnEscape); + } + const toggle = () => { opened.value = !opened.value; } + + watch(opened, () => { + if (!opened.value) { + document.removeEventListener('click', close); + document.removeEventListener('keypress', closeOnEscape); + } + }); + + return () => ( +
+ { + context.slots.context({ + opened, + toggle, + close, + open, + }) || + Menu + } + { + // Make sure clicks inside do not autoclose this + }} + > + {context.slots.default({ + opened, + toggle, + close, + open, + }) || null} + +
+ ); + }, +}); diff --git a/packages/core/lib/components/MainMenu.scss b/packages/core/lib/components/MainMenu.scss new file mode 100644 index 0000000..5fa52af --- /dev/null +++ b/packages/core/lib/components/MainMenu.scss @@ -0,0 +1,2 @@ +.sb-main-menu { +} diff --git a/packages/core/lib/components/MainMenu.tsx b/packages/core/lib/components/MainMenu.tsx new file mode 100644 index 0000000..21beba1 --- /dev/null +++ b/packages/core/lib/components/MainMenu.tsx @@ -0,0 +1,34 @@ +import { + defineComponent, + PropType, +} from 'vue'; +import { Block } from '../blocks'; + +import { TreeBlockSelect } from './TreeBlockSelect'; + +import './MainMenu.scss'; + +interface MainMenuProps { + block: Block; +} + +export const SbMainMenu = defineComponent({ + name: 'sb-main-menu', + + props: { + block: { + type: (null as unknown) as PropType, + required: true, + }, + }, + + setup(props: MainMenuProps, context) { + return () => ( +
+ +
+ ); + }, +}); diff --git a/packages/core/lib/components/Modal.tsx b/packages/core/lib/components/Modal.tsx index 8fab387..71c5b28 100644 --- a/packages/core/lib/components/Modal.tsx +++ b/packages/core/lib/components/Modal.tsx @@ -7,7 +7,7 @@ import './Modal.scss'; interface ModalProps { open: boolean; - eventClose: () => void; + onClose: () => void; } export const SbModal = defineComponent({ @@ -18,7 +18,7 @@ export const SbModal = defineComponent({ type: Boolean, default: false, }, - eventClose: { type: Function, default: () => {} }, + onClose: { type: Function, default: () => {} }, }, setup(props: ModalProps, context) { @@ -33,7 +33,7 @@ export const SbModal = defineComponent({ class="sb-modal__overlay" onClick={($event: MouseEvent) => { $event.stopPropagation(); - props.eventClose(); + props.onClose(); }} >
diff --git a/packages/core/lib/components/Schlechtenburg.tsx b/packages/core/lib/components/Schlechtenburg.tsx index 680b90d..9fae3e7 100644 --- a/packages/core/lib/components/Schlechtenburg.tsx +++ b/packages/core/lib/components/Schlechtenburg.tsx @@ -18,13 +18,14 @@ import { BlockLibrary } from '../use-dynamic-blocks'; import { EditorDimensions, useResizeObserver } from '../use-resize-observer'; import { ActiveBlock } from '../use-activation'; +import { SbMainMenu } from './MainMenu'; import { SbBlock } from './Block'; import './Schlechtenburg.scss'; export interface SchlechtenburgProps { customBlocks: BlockDefinition[]; - eventUpdate: (b: Block) => void; + onUpdate: (b: Block) => void; block: Block; mode: SbMode; } @@ -37,7 +38,7 @@ export const Schlechtenburg = defineComponent({ props: { customBlocks: { type: Array as PropType, default: () => [] }, block: { type: Object as PropType>, required: true }, - eventUpdate: { type: Function, default: () => {} }, + onUpdate: { type: Function, default: () => {} }, mode: { type: String as PropType, validator(value: any) { @@ -75,9 +76,12 @@ export const Schlechtenburg = defineComponent({ class="sb-main" ref={el} > +
); diff --git a/packages/core/lib/components/TreeBlockSelect.scss b/packages/core/lib/components/TreeBlockSelect.scss new file mode 100644 index 0000000..ad59f14 --- /dev/null +++ b/packages/core/lib/components/TreeBlockSelect.scss @@ -0,0 +1,2 @@ +.sb-tree-block-select { +} diff --git a/packages/core/lib/components/TreeBlockSelect.tsx b/packages/core/lib/components/TreeBlockSelect.tsx new file mode 100644 index 0000000..a77e403 --- /dev/null +++ b/packages/core/lib/components/TreeBlockSelect.tsx @@ -0,0 +1,38 @@ +import { + defineComponent, + PropType, +} from 'vue'; +import { Block } from '../blocks'; +import { SbContextMenu } from './ContextMenu'; +import { SbButton } from './Button'; + +import './TreeBlockSelect.scss'; + +interface TreeBlockSelectProps { + block: Block; +} + +export const SbTreeBlockSelect = defineComponent({ + name: 'sb-main-menu', + + props: { + block: { + type: (null as unknown) as PropType, + required: true, + }, + }, + + setup(props: TreeBlockSelectProps, context) { + return () => ( + Tree, + default: () =>
    +
  • Test
  • +
, + }} + >
+ ); + }, +}); diff --git a/packages/image/lib/edit.tsx b/packages/image/lib/edit.tsx index 2dc60e5..b176c8a 100644 --- a/packages/image/lib/edit.tsx +++ b/packages/image/lib/edit.tsx @@ -29,7 +29,7 @@ export default defineComponent({ props: { ...blockProps, - eventUpdate: { type: Function, default: () => {} }, + onUpdate: { type: Function, default: () => {} }, data: { type: (null as unknown) as PropType, default: getDefaultData, @@ -61,7 +61,7 @@ export default defineComponent({ if (fileInput.value && fileInput.value.files && fileInput.value.files.length) { const reader = new FileReader(); reader.addEventListener('load', () => { - props.eventUpdate({ + props.onUpdate({ src: reader.result, alt: props.data.alt, description: props.data.description, @@ -73,7 +73,7 @@ export default defineComponent({ }; const onDescriptionUpdate = (description) => { - props.eventUpdate({ + props.onUpdate({ ...props.data, description, }); @@ -101,7 +101,7 @@ export default defineComponent({ /> onDescriptionUpdate(updated)} + onUpdate={(updated: Block) => onDescriptionUpdate(updated)} /> : Select Image} diff --git a/packages/layout/lib/edit.tsx b/packages/layout/lib/edit.tsx index ed47adb..a0c6f61 100644 --- a/packages/layout/lib/edit.tsx +++ b/packages/layout/lib/edit.tsx @@ -33,7 +33,7 @@ export default defineComponent({ props: { ...blockProps, - eventUpdate: { type: Function, default: () => {} }, + onUpdate: { type: Function, default: () => {} }, data: { type: (null as unknown) as PropType, default: getDefaultData, @@ -60,7 +60,7 @@ export default defineComponent({ const toggleOrientation = () => { console.log('toggle'); - props.eventUpdate({ + props.onUpdate({ orientation: localData.orientation === 'vertical' ? 'horizontal' : 'vertical', }); }; @@ -70,7 +70,7 @@ export default defineComponent({ if (index === -1) { return; } - props.eventUpdate({ + props.onUpdate({ children: [ ...localData.children.slice(0, index), { @@ -87,7 +87,7 @@ export default defineComponent({ ...localData.children, block, ]; - props.eventUpdate({ children: [...localData.children] }); + props.onUpdate({ children: [...localData.children] }); activate(block.blockId); }; @@ -97,7 +97,7 @@ export default defineComponent({ block, ...localData.children.slice(index + 1), ]; - props.eventUpdate({ children: [...localData.children] }); + props.onUpdate({ children: [...localData.children] }); activate(block.blockId); }; @@ -106,13 +106,25 @@ export default defineComponent({ ...localData.children.slice(0, index), ...localData.children.slice(index + 1), ]; - props.eventUpdate({ children: [...localData.children] }); + props.onUpdate({ children: [...localData.children] }); const newActiveIndex = Math.max(index - 1, 0); activate(localData.children[newActiveIndex].blockId); }; - const moveUp = (index: number) => { + const activateBlock = (index: number) => { + const safeIndex = + Math.max( + Math.min( + localData.children.length - 1, + index, + ), + 0, + ); + activate(localData.children[safeIndex].blockId); + }; + + const moveBackward = (index: number) => { if (index === 0) { return; } @@ -126,10 +138,10 @@ export default defineComponent({ ...localData.children.slice(index + 1), ]; - props.eventUpdate({ children: [...localData.children] }); + props.onUpdate({ children: [...localData.children] }); }; - const moveDown = (index: number) => { + const moveForward = (index: number) => { if (index === localData.children.length - 1) { return; } @@ -143,7 +155,7 @@ export default defineComponent({ ...localData.children.slice(index + 2), ]; - props.eventUpdate({ children: [...localData.children] }); + props.onUpdate({ children: [...localData.children] }); }; return () => ( @@ -160,17 +172,19 @@ export default defineComponent({ {...{ key: child.blockId }} data-order={index} block={child} - eventUpdate={(updated: Block) => onChildUpdate(child, updated)} - eventPrependBlock={(block: Block) => insertBlock(index - 1, block)} - eventAppendBlock={(block: Block) => insertBlock(index, block)} - removable + onUpdate={(updated: Block) => onChildUpdate(child, updated)} + onRemoveSelf={() => removeBlock(index)} + onPrependBlock={(block: Block) => insertBlock(index - 1, block)} + onAppendBlock={(block: Block) => insertBlock(index, block)} + onActivatePrevious={(block: Block) => activateBlock(index - 1,)} + onActivateNext={(block: Block) => activateBlock(index + 1,)} > {{ 'context-toolbar': () => moveUp(index)} - eventMoveDown={() => moveDown(index)} - eventRemoveBlock={() => removeBlock(index)} + onMoveBackward={() => moveBackward(index)} + onMoveForward={() => moveForward(index)} + onRemove={() => removeBlock(index)} sortable={props.sortable} />, }} diff --git a/packages/paragraph/lib/edit.tsx b/packages/paragraph/lib/edit.tsx index 5fa5db9..b0a4db2 100644 --- a/packages/paragraph/lib/edit.tsx +++ b/packages/paragraph/lib/edit.tsx @@ -28,9 +28,11 @@ import './style.scss'; interface ParagraphProps extends BlockProps { data: ParagraphData; - eventUpdate: (b?: ParagraphData) => void; - eventAppendBlock: (b?: BlockData) => void; - eventRemoveBlock: () => void; + onUpdate: (b?: ParagraphData) => void; + onAppendBlock: (b?: BlockData) => void; + onRemoveSelf: () => void; + onActivateNext: () => void; + onActivatePrevious: () => void; } export default defineComponent({ @@ -44,9 +46,11 @@ export default defineComponent({ type: (null as unknown) as PropType, default: getDefaultData, }, - eventUpdate: { type: Function, default: () => {} }, - eventAppendBlock: { type: Function, default: () => {} }, - eventRemoveBlock: { type: Function, default: () => {} }, + onUpdate: { type: Function, default: () => {} }, + onAppendBlock: { type: Function, default: () => {} }, + onRemoveSelf: { type: Function, default: () => {} }, + onActivateNext: { type: Function, default: () => {} }, + onActivatePrevious: { type: Function, default: () => {} }, }, setup(props: ParagraphProps) { @@ -98,7 +102,7 @@ export default defineComponent({ })); const setAlignment = ($event: Event) => { - props.eventUpdate({ + props.onUpdate({ value: localData.value, align: ($event.target as HTMLSelectElement).value, }); @@ -111,16 +115,16 @@ export default defineComponent({ const onBlur = () => { localData.focused = false; - props.eventUpdate({ + props.onUpdate({ value: localData.value, align: localData.align, }); }; const onKeydown = ($event: KeyboardEvent) => { - if (props.eventAppendBlock && $event.key === 'Enter' && !$event.shiftKey) { + if ($event.key === 'Enter' && !$event.shiftKey) { const blockId = `${+(new Date())}`; - props.eventAppendBlock({ + props.onAppendBlock({ blockId, name: 'sb-paragraph', data: getDefaultData(), @@ -133,8 +137,23 @@ export default defineComponent({ }; const onKeyup = ($event: KeyboardEvent) => { - if (props.eventRemoveBlock && $event.key === 'Backspace' && localData.value === '') { - props.eventRemoveBlock(); + if ($event.key === 'Backspace' && localData.value === '') { + props.onRemoveSelf(); + } + + const selection = window.getSelection(); + const node = selection.focusNode; + const childNodes = Array.from(inputEl.value.childNodes); + const index = childNodes.indexOf(node); + if (node === inputEl.value || index === 0 || index === childNodes.length -1) { + switch ($event.key) { + case 'ArrowDown': + props.onActivateNext(); + break; + case 'ArrowUp': + props.onActivatePrevious(); + break; + } } }; diff --git a/src/App.tsx b/src/App.tsx index 0f31c02..2bd8549 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,12 +8,10 @@ import { import { Schlechtenburg, Block, SbMode } from '../packages/core/lib'; -/* +import SbLayout from '../packages/layout/lib'; import SbHeading from '../packages/heading/lib'; import SbParagraph from '../packages/paragraph/lib'; import SbImage from '../packages/image/lib'; - */ -import SbLayout from '../packages/layout/lib'; import './App.scss'; @@ -41,16 +39,14 @@ export default defineComponent({ case SbMode.Edit: return ) => { + onUpdate={(newBlock: Block) => { block.data = newBlock.data; }} customBlocks={[ SbLayout, - /* SbHeading, SbImage, SbParagraph, - */ ]} key="edit" mode="edit" @@ -67,7 +63,6 @@ export default defineComponent({ }); return () => { - console.log('render App'); return