Added basic tree discovery
This commit is contained in:
parent
d76f40cf7d
commit
7d6a3730c6
|
@ -1,10 +1,18 @@
|
|||
import { Component } from 'vue';
|
||||
|
||||
export interface BlockTree {
|
||||
name: string;
|
||||
icon?: string;
|
||||
children?: BlockTree[];
|
||||
}
|
||||
|
||||
export interface BlockDefinition {
|
||||
name: string;
|
||||
icon?: string;
|
||||
getDefaultData: any;
|
||||
edit: Component;
|
||||
display: Component;
|
||||
getChildren?: (block: Block) => Block[],
|
||||
}
|
||||
|
||||
export interface BlockLibraryDefinition {
|
||||
|
|
|
@ -73,7 +73,7 @@ export const SbBlock = defineComponent({
|
|||
};
|
||||
|
||||
return () => {
|
||||
const BlockComponent = getBlock(props.block.name) as any;
|
||||
const BlockComponent = getBlock(props.block.name)?.[mode.value] as any;
|
||||
|
||||
if (!BlockComponent) {
|
||||
const MissingBlock = SbMissingBlock[mode.value];
|
||||
|
@ -84,12 +84,10 @@ export const SbBlock = defineComponent({
|
|||
}
|
||||
|
||||
if (mode.value === SbMode.Display) {
|
||||
return () => (
|
||||
<BlockComponent
|
||||
data={props.block.data}
|
||||
blockId={props.block.blockId}
|
||||
/>
|
||||
);
|
||||
return <BlockComponent
|
||||
data={props.block.data}
|
||||
blockId={props.block.blockId}
|
||||
/>;
|
||||
}
|
||||
|
||||
return <div
|
||||
|
|
|
@ -31,7 +31,6 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
setup(props: MissingBlockProps) {
|
||||
console.log(props, props.name, props.data, props.blockId);
|
||||
return () => (
|
||||
<div class="sb-missing-block">
|
||||
Missing block: {props.name}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
.sb-context {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sb-context-menu {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
background: var(--grey-0);
|
||||
border: 1px solid var(--grey-3);
|
||||
top: 100%;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
z-index: var(--z-context-menu);
|
||||
|
||||
&[open] {
|
||||
display: flex;
|
||||
}
|
||||
}
|
|
@ -23,28 +23,33 @@ export const SbContextMenu = defineComponent({
|
|||
|
||||
setup(props: ContextMenuProps, context) {
|
||||
const opened = ref(false);
|
||||
const close = () => { opened.value = false; }
|
||||
const open = () => { opened.value = true; };
|
||||
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; }
|
||||
const toggle = () => { opened.value ? close() : open() };
|
||||
|
||||
watch(opened, () => {
|
||||
if (!opened.value) {
|
||||
document.removeEventListener('click', close);
|
||||
document.removeEventListener('keypress', closeOnEscape);
|
||||
watch(opened, (curr, prev) => {
|
||||
if (curr === prev) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!curr) {
|
||||
document.body.removeEventListener('click', close);
|
||||
document.body.removeEventListener('keypress', closeOnEscape);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
document.body.addEventListener('click', close);
|
||||
document.body.addEventListener('keypress', closeOnEscape);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return () => (
|
||||
<div class="sb-context-menu">
|
||||
<div class="sb-context">
|
||||
{
|
||||
context.slots.context({
|
||||
opened,
|
||||
|
@ -55,10 +60,12 @@ export const SbContextMenu = defineComponent({
|
|||
<SbButton onClick={toggle}>Menu</SbButton>
|
||||
}
|
||||
<dialog
|
||||
open={opened.value}
|
||||
class="sb-context-menu"
|
||||
open={opened.value ? true : undefined}
|
||||
onClose={close}
|
||||
onClick={($event: Event) => {
|
||||
// Make sure clicks inside do not autoclose this
|
||||
$event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{context.slots.default({
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
} from 'vue';
|
||||
import { Block } from '../blocks';
|
||||
|
||||
import { TreeBlockSelect } from './TreeBlockSelect';
|
||||
import { SbTreeBlockSelect } from './TreeBlockSelect';
|
||||
|
||||
import './MainMenu.scss';
|
||||
|
||||
|
@ -25,8 +25,8 @@ export const SbMainMenu = defineComponent({
|
|||
setup(props: MainMenuProps, context) {
|
||||
return () => (
|
||||
<div class="sb-main-menu">
|
||||
<TreeBlockSelect
|
||||
block={block}
|
||||
<SbTreeBlockSelect
|
||||
block={props.block}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -67,18 +67,16 @@ export const Schlechtenburg = defineComponent({
|
|||
|
||||
provide(BlockLibrary, blockLibrary);
|
||||
|
||||
watch(props.block, () => {
|
||||
console.log('Update', props.block);
|
||||
});
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class="sb-main"
|
||||
ref={el}
|
||||
>
|
||||
<SbMainMenu
|
||||
block={props.block}
|
||||
/>
|
||||
{
|
||||
mode.value === SbMode.Edit
|
||||
? <SbMainMenu block={props.block} />
|
||||
: null
|
||||
}
|
||||
<SbBlock
|
||||
block={props.block}
|
||||
onUpdate={props.onUpdate}
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
PropType,
|
||||
} from 'vue';
|
||||
import { Block } from '../blocks';
|
||||
import {
|
||||
Block,
|
||||
BlockTree,
|
||||
} from '../blocks';
|
||||
import { useDynamicBlocks } from '../use-dynamic-blocks';
|
||||
|
||||
import { SbContextMenu } from './ContextMenu';
|
||||
import { SbButton } from './Button';
|
||||
|
||||
|
@ -23,16 +29,34 @@ export const SbTreeBlockSelect = defineComponent({
|
|||
},
|
||||
|
||||
setup(props: TreeBlockSelectProps, context) {
|
||||
const { getBlock } = useDynamicBlocks();
|
||||
|
||||
const getTreeForBlock = (block: Block): BlockTree => {
|
||||
const getBlockChildren = getBlock(block.name)?.getChildren;
|
||||
// TODO: vue-jxs apparently cannot parse arrow functions here
|
||||
const getChildren = getBlockChildren || function ({ data }) { return data?.children; };
|
||||
const children = getChildren(block) || [];
|
||||
return {
|
||||
name: block.name,
|
||||
children: children.map(getTreeForBlock),
|
||||
};
|
||||
};
|
||||
|
||||
const tree = computed(() => getTreeForBlock(props.block));
|
||||
|
||||
const treeToHtml = (tree: BlockTree) => <li>
|
||||
{tree.name}
|
||||
{tree.children.length ? <ul>{tree.children.map(treeToHtml)}</ul> : null}
|
||||
</li>;
|
||||
|
||||
return () => (
|
||||
<SbContextMenu
|
||||
class="sb-tree-block-select"
|
||||
v-slots={{
|
||||
context: ({ toggle }) => <SbButton onClick={toggle}>Tree</SbButton>,
|
||||
default: () => <ul>
|
||||
<li>Test</li>
|
||||
</ul>,
|
||||
default: () => <ul>{treeToHtml(tree.value)}</ul>,
|
||||
}}
|
||||
></SbContextMenu>
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ export const BlockLibrary = Symbol('Schlechtenburg block library');
|
|||
export function useDynamicBlocks() {
|
||||
const mode = inject(Mode, ref(SbMode.Edit));
|
||||
const customBlocks: BlockLibraryDefinition = inject(BlockLibrary, reactive({}));
|
||||
const getBlock = (name: string) => customBlocks[name]?.[mode.value];
|
||||
const getBlock = (name: string) => customBlocks[name];
|
||||
|
||||
return {
|
||||
mode,
|
||||
|
|
|
@ -6,4 +6,5 @@ export default {
|
|||
getDefaultData,
|
||||
edit: defineAsyncComponent(() => import('./edit')),
|
||||
display: defineAsyncComponent(() => import('./display')),
|
||||
getChildren: (block) => [ block.data.description ],
|
||||
};
|
||||
|
|
12
src/App.tsx
12
src/App.tsx
|
@ -49,13 +49,19 @@ export default defineComponent({
|
|||
SbParagraph,
|
||||
]}
|
||||
key="edit"
|
||||
mode="edit"
|
||||
mode={SbMode.Edit}
|
||||
/>;
|
||||
case SbMode.Edit:
|
||||
case SbMode.Display:
|
||||
return <Schlechtenburg
|
||||
block={block}
|
||||
customBlocks={[
|
||||
SbLayout,
|
||||
SbHeading,
|
||||
SbImage,
|
||||
SbParagraph,
|
||||
]}
|
||||
key="display"
|
||||
mode="display"
|
||||
mode={SbMode.Display}
|
||||
/>;
|
||||
case 'data':
|
||||
return <pre><code>{ JSON.stringify(block, null, 2) }</code></pre>;
|
||||
|
|
|
@ -22,6 +22,8 @@ html {
|
|||
--fg: var(--black);
|
||||
|
||||
--interact: #3f9cff;
|
||||
|
||||
--z-context-menu: 3000;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
Loading…
Reference in a new issue