Slightly better interface
This commit is contained in:
parent
d00383892f
commit
b81f0c6673
|
@ -3,5 +3,4 @@
|
|||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
|
36
src/App.tsx
36
src/App.tsx
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
defineComponent,
|
||||
reactive,
|
||||
watchEffect,
|
||||
ref,
|
||||
} from '@vue/composition-api';
|
||||
import Schlechtenburg from '@components/Schlechtenburg';
|
||||
import { BlockData } from './components/TreeElement';
|
||||
|
@ -12,6 +12,7 @@ export default defineComponent({
|
|||
name: 'App',
|
||||
|
||||
setup() {
|
||||
const activeTab = ref('edit');
|
||||
const block = reactive({
|
||||
name: 'sb-layout',
|
||||
blockId: `${+(new Date())}`,
|
||||
|
@ -23,20 +24,39 @@ export default defineComponent({
|
|||
|
||||
return () => (
|
||||
<div id="app">
|
||||
<Schlechtenburg
|
||||
block={block}
|
||||
<select
|
||||
value={activeTab.value}
|
||||
{...{
|
||||
on: {
|
||||
update: (newBlock: BlockData) => {
|
||||
block.name = newBlock.name;
|
||||
block.blockId = newBlock.blockId;
|
||||
block.data = newBlock.data;
|
||||
change: ($event: Event) => {
|
||||
activeTab.value = ($event.target as HTMLSelectElement).value;
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<option>edit</option>
|
||||
<option>display</option>
|
||||
<option>json</option>
|
||||
</select>
|
||||
<Schlechtenburg
|
||||
vShow={activeTab.value === 'edit'}
|
||||
block={block}
|
||||
eventUpdate={(newBlock: BlockData) => {
|
||||
block.name = newBlock.name;
|
||||
block.blockId = newBlock.blockId;
|
||||
block.data = newBlock.data;
|
||||
}}
|
||||
/>
|
||||
|
||||
<pre><code>{JSON.stringify(block, null, 2)}</code></pre>
|
||||
<Schlechtenburg
|
||||
vShow={activeTab.value === 'display'}
|
||||
block={block}
|
||||
mode="display"
|
||||
/>
|
||||
|
||||
<pre vShow={activeTab.value === 'json'}>
|
||||
<code>{JSON.stringify(block, null, 2)}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
5
src/components/Schlechtenburg.scss
Normal file
5
src/components/Schlechtenburg.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
.sb-main {
|
||||
padding: 50px 20px;
|
||||
|
||||
background-color: var(--bg);
|
||||
}
|
|
@ -9,6 +9,8 @@ import {
|
|||
model,
|
||||
ActiveBlock,
|
||||
BlockData,
|
||||
SbMode,
|
||||
Mode,
|
||||
BlockDefinition,
|
||||
BlockLibraryDefinition,
|
||||
BlockLibrary,
|
||||
|
@ -21,9 +23,13 @@ import SbParagraph from '@user/Paragraph/index';
|
|||
import SbImage from '@user/Image/index';
|
||||
import SbHeading from '@user/Heading/index';
|
||||
|
||||
import './Schlechtenburg.scss';
|
||||
|
||||
export interface SchlechtenburgProps {
|
||||
customBlocks: BlockDefinition[];
|
||||
eventUpdate: (b?: BlockData) => void;
|
||||
block: BlockData;
|
||||
mode: SbMode;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -32,11 +38,25 @@ export default defineComponent({
|
|||
model,
|
||||
|
||||
props: {
|
||||
customBlocks: { type: (null as unknown) as PropType<BlockDefinition[]>, default: () => [] },
|
||||
block: { type: (null as unknown) as PropType<BlockData>, required: true },
|
||||
customBlocks: { type: Array as PropType<BlockDefinition[]>, default: () => [] },
|
||||
block: { type: Object as PropType<BlockData>, required: true },
|
||||
eventUpdate: {
|
||||
type: (Function as unknown) as (b?: BlockData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
validator(value: string) {
|
||||
return ['edit', 'display'].includes(value);
|
||||
},
|
||||
default: 'edit',
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, context) {
|
||||
setup(props: SchlechtenburgProps, context) {
|
||||
const mode = ref(props.mode);
|
||||
provide(Mode, mode);
|
||||
|
||||
const activeBlock = ref(null);
|
||||
provide(ActiveBlock, activeBlock);
|
||||
|
||||
|
@ -56,15 +76,12 @@ export default defineComponent({
|
|||
provide(BlockLibrary, blockLibrary);
|
||||
|
||||
return () => (
|
||||
<SbBlock
|
||||
class="sb-main"
|
||||
block={props.block}
|
||||
{...{
|
||||
on: {
|
||||
update: (block: BlockDefinition) => context.emit('update', block),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div class="sb-main">
|
||||
<SbBlock
|
||||
block={props.block}
|
||||
eventUpdate={props.eventUpdate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -6,9 +6,6 @@ import {
|
|||
computed,
|
||||
} from '@vue/composition-api';
|
||||
|
||||
export const ActiveBlock = Symbol('Schlechtenburg active block');
|
||||
export const BlockLibrary = Symbol('Schlechtenburg block library');
|
||||
|
||||
export interface BlockDefinition {
|
||||
name: string;
|
||||
getDefaultData: any;
|
||||
|
@ -38,16 +35,32 @@ export const model = {
|
|||
|
||||
export const blockProps = {
|
||||
blockId: { type: String, required: true },
|
||||
eventUpdate: {
|
||||
type: (Function as unknown) as (b: any) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
data: { type: Object, default: () => ({}) },
|
||||
};
|
||||
|
||||
export enum SbMode {
|
||||
Edit = 'edit',
|
||||
Display = 'display',
|
||||
}
|
||||
export const Mode = Symbol('Schlechtenburg mode');
|
||||
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];
|
||||
const getBlock = (name: string) => customBlocks[name][mode.value];
|
||||
|
||||
return { customBlocks, getBlock };
|
||||
return {
|
||||
mode,
|
||||
customBlocks,
|
||||
getBlock,
|
||||
};
|
||||
}
|
||||
|
||||
export const ActiveBlock = Symbol('Schlechtenburg active block');
|
||||
export function useActivation(currentBlockId: string) {
|
||||
const activeBlockId: Ref<string|null> = inject(ActiveBlock, ref(null));
|
||||
const isActive = computed(() => activeBlockId.value === currentBlockId);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
justify-items: stretch;
|
||||
min-height: 50px;
|
||||
|
||||
> .sb-toolbar {
|
||||
> * > .sb-toolbar {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
@ -13,10 +13,9 @@
|
|||
&_active {
|
||||
outline: 1px solid var(--grey-2);
|
||||
|
||||
> .sb-toolbar {
|
||||
> * > .sb-toolbar {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
outline: 1px solid var(--grey-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
BlockData,
|
||||
useDynamicBlocks,
|
||||
useActivation,
|
||||
BlockDefinition,
|
||||
} from '@components/TreeElement';
|
||||
|
||||
import './Block.scss';
|
||||
|
@ -17,6 +16,18 @@ export default defineComponent({
|
|||
|
||||
props: {
|
||||
block: { type: (null as unknown) as PropType<BlockData>, required: true },
|
||||
eventUpdate: {
|
||||
type: (Function as unknown) as (b?: BlockData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
eventInsertBlock: {
|
||||
type: (Function as unknown) as (b?: BlockData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
eventAppendBlock: {
|
||||
type: (Function as unknown) as (b?: BlockData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, context) {
|
||||
|
@ -28,7 +39,7 @@ export default defineComponent({
|
|||
}));
|
||||
|
||||
const onChildUpdate = (updated: {[key: string]: any}) => {
|
||||
context.emit('update', {
|
||||
props.eventUpdate({
|
||||
...props.block,
|
||||
data: {
|
||||
...props.block.data,
|
||||
|
@ -37,27 +48,31 @@ export default defineComponent({
|
|||
});
|
||||
};
|
||||
|
||||
const Block = getBlock(props.block.name).edit as any;
|
||||
const Block = getBlock(props.block.name) as any;
|
||||
|
||||
return () => <Block
|
||||
class={classes.value}
|
||||
data={props.block.data}
|
||||
block-id={props.block.blockId}
|
||||
{...{
|
||||
attrs: context.attrs,
|
||||
on: {
|
||||
...context.listeners,
|
||||
update: onChildUpdate,
|
||||
'insert-block': (block: BlockDefinition) => context.emit('insert-block', block),
|
||||
'append-block': (block: BlockDefinition) => context.emit('append-block', block),
|
||||
},
|
||||
nativeOn: {
|
||||
click: ($event: MouseEvent) => {
|
||||
$event.stopPropagation();
|
||||
activate();
|
||||
return () => <div class={classes.value}>
|
||||
<div class="sb-block__edit-cover"></div>
|
||||
<div class="sb-block__mover"></div>
|
||||
<Block
|
||||
data={props.block.data}
|
||||
block-id={props.block.blockId}
|
||||
eventUpdate={onChildUpdate}
|
||||
eventInsertBlock={props.eventInsertBlock}
|
||||
eventAppendBlock={props.eventAppendBlock}
|
||||
{...{
|
||||
attrs: context.attrs,
|
||||
on: {
|
||||
...context.listeners,
|
||||
update: onChildUpdate,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>;
|
||||
nativeOn: {
|
||||
click: ($event: MouseEvent) => {
|
||||
$event.stopPropagation();
|
||||
activate();
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
.sb-block-picker {
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
import { computed, defineComponent } from '@vue/composition-api';
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
ref,
|
||||
} from '@vue/composition-api';
|
||||
import {
|
||||
useDynamicBlocks,
|
||||
BlockDefinition,
|
||||
} from '../TreeElement';
|
||||
|
||||
import SbButton from './Button';
|
||||
import SbModal from './Modal';
|
||||
|
||||
import './BlockPicker.scss';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -12,26 +19,45 @@ export default defineComponent({
|
|||
props: {},
|
||||
|
||||
setup(props, context) {
|
||||
const open = ref(false);
|
||||
const { customBlocks } = useDynamicBlocks();
|
||||
|
||||
const blockList = computed(() => Object.keys(customBlocks).map((key) => customBlocks[key]));
|
||||
|
||||
const selectBlock = (block: BlockDefinition) => () => {
|
||||
open.value = false;
|
||||
context.emit('picked-block', {
|
||||
name: block.name,
|
||||
blockId: `${+(new Date())}`,
|
||||
data: block.getDefaultData(),
|
||||
});
|
||||
};
|
||||
|
||||
return () => (
|
||||
<div class="sb-block-picker">
|
||||
{...blockList.value.map((block: BlockDefinition) => (
|
||||
<button
|
||||
type="button"
|
||||
{...{
|
||||
on: {
|
||||
click: () => context.emit('picked-block', {
|
||||
name: block.name,
|
||||
blockId: `${+(new Date())}`,
|
||||
data: block.getDefaultData(),
|
||||
}),
|
||||
},
|
||||
}}
|
||||
>{block.name}</button>
|
||||
))}
|
||||
<div
|
||||
class="sb-block-picker"
|
||||
onClick={($event: MouseEvent) => $event.stopPropagation()}
|
||||
>
|
||||
<SbButton
|
||||
type="button"
|
||||
onClick={() => {
|
||||
open.value = true;
|
||||
console.log(open);
|
||||
}}
|
||||
>Add a block</SbButton>
|
||||
<SbModal
|
||||
open={open.value}
|
||||
eventClose={() => {
|
||||
open.value = false;
|
||||
}}
|
||||
>
|
||||
{...blockList.value.map((block: BlockDefinition) => (
|
||||
<SbButton
|
||||
type="button"
|
||||
onClick={selectBlock(block)}
|
||||
>{block.name}</SbButton>
|
||||
))}
|
||||
</SbModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
10
src/components/internal/Button.scss
Normal file
10
src/components/internal/Button.scss
Normal file
|
@ -0,0 +1,10 @@
|
|||
.sb-button {
|
||||
border: 0;
|
||||
padding: 8px 12px;
|
||||
background-color: var(--grey-0);
|
||||
border: 1px solid var(--grey-2);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid var(--interact);
|
||||
}
|
||||
}
|
23
src/components/internal/Button.tsx
Normal file
23
src/components/internal/Button.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { defineComponent } from '@vue/composition-api';
|
||||
|
||||
import './Button.scss';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'sb-button',
|
||||
|
||||
inheritAttrs: false,
|
||||
|
||||
setup(props, context) {
|
||||
return () => (
|
||||
<button
|
||||
class="sb-button"
|
||||
{...{
|
||||
attrs: context.attrs,
|
||||
on: context.listeners,
|
||||
}}
|
||||
>
|
||||
{context.slots.default()}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
});
|
31
src/components/internal/Modal.scss
Normal file
31
src/components/internal/Modal.scss
Normal file
|
@ -0,0 +1,31 @@
|
|||
.sb-modal {
|
||||
&__overlay {
|
||||
background-color: var(--grey-3-t);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 10vh 10vw;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&__content {
|
||||
width: 900px;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
background-color: var(--grey-0);
|
||||
padding: 24px 32px;
|
||||
}
|
||||
|
||||
&_open #{&}__overlay {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
45
src/components/internal/Modal.tsx
Normal file
45
src/components/internal/Modal.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
} from '@vue/composition-api';
|
||||
|
||||
import './Modal.scss';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'sb-modal',
|
||||
|
||||
props: {
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
eventClose: {
|
||||
type: (Function as unknown) as () => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, context) {
|
||||
const classes = computed(() => ({
|
||||
'sb-modal': true,
|
||||
'sb-modal_open': props.open,
|
||||
}));
|
||||
|
||||
return () => (
|
||||
<div class={classes.value}>
|
||||
<div
|
||||
class="sb-modal__overlay"
|
||||
onClick={($event: MouseEvent) => {
|
||||
$event.stopPropagation();
|
||||
props.eventClose();
|
||||
}}
|
||||
>
|
||||
<div class="sb-modal__content">
|
||||
{context.slots.default()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
.sb-toolbar {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: var(--grey-1);
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
|
||||
setup(props: ImageProps) {
|
||||
setup(props: ImageProps, context) {
|
||||
const localData = reactive({
|
||||
src: props.data.src,
|
||||
alt: props.data.alt,
|
||||
|
@ -56,7 +56,9 @@ export default defineComponent({
|
|||
|
||||
const onImageSelect = () => {
|
||||
if (fileInput.value && fileInput.value.files && fileInput.value.files.length) {
|
||||
localData.src = window.URL.createObjectURL(fileInput.value.files[0]);
|
||||
context.emit('update', {
|
||||
src: window.URL.createObjectURL(fileInput.value.files[0]),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
57
src/components/user/Layout/display.tsx
Normal file
57
src/components/user/Layout/display.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import {
|
||||
reactive,
|
||||
computed,
|
||||
defineComponent,
|
||||
watch,
|
||||
PropType,
|
||||
} from '@vue/composition-api';
|
||||
import {
|
||||
model,
|
||||
blockProps,
|
||||
useActivation,
|
||||
BlockData,
|
||||
} from '@components/TreeElement';
|
||||
|
||||
import SbBlock from '@internal/Block';
|
||||
import SbToolbar from '@internal/Toolbar';
|
||||
import SbBlockPlaceholder from '@internal/BlockPlaceholder';
|
||||
|
||||
import {
|
||||
LayoutData,
|
||||
LayoutProps,
|
||||
getDefaultData,
|
||||
} from './util';
|
||||
|
||||
import './style.scss';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'sb-layout-display',
|
||||
|
||||
model,
|
||||
|
||||
props: {
|
||||
...blockProps,
|
||||
data: {
|
||||
type: (null as unknown) as PropType<LayoutData>,
|
||||
default: getDefaultData,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props: LayoutProps, context) {
|
||||
const classes = computed(() => ({
|
||||
'sb-layout': true,
|
||||
[`sb-layout_${props.data.orientation}`]: true,
|
||||
}));
|
||||
|
||||
return () => (
|
||||
<div class={classes.value}>
|
||||
{...props.data.children.map((child, index) => (
|
||||
<SbBlock
|
||||
key={child.blockId}
|
||||
block={child}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -7,12 +7,13 @@ import {
|
|||
} from '@vue/composition-api';
|
||||
import {
|
||||
model,
|
||||
blockProps,
|
||||
useActivation,
|
||||
BlockData,
|
||||
blockProps,
|
||||
} from '@components/TreeElement';
|
||||
|
||||
import SbBlock from '@internal/Block';
|
||||
import SbButton from '@internal/Button';
|
||||
import SbToolbar from '@internal/Toolbar';
|
||||
import SbBlockPlaceholder from '@internal/BlockPlaceholder';
|
||||
|
||||
|
@ -31,6 +32,10 @@ export default defineComponent({
|
|||
|
||||
props: {
|
||||
...blockProps,
|
||||
eventUpdate: {
|
||||
type: (Function as unknown) as (b?: LayoutData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
data: {
|
||||
type: (null as unknown) as PropType<LayoutData>,
|
||||
default: getDefaultData,
|
||||
|
@ -56,14 +61,14 @@ export default defineComponent({
|
|||
}));
|
||||
|
||||
const toggleOrientation = () => {
|
||||
context.emit('update', {
|
||||
props.eventUpdate({
|
||||
orientation: localData.orientation === 'vertical' ? 'horizontal' : 'vertical',
|
||||
});
|
||||
};
|
||||
|
||||
const onChildUpdate = (child: BlockData, updated: BlockData) => {
|
||||
const index = localData.children.indexOf(child);
|
||||
context.emit('update', {
|
||||
props.eventUpdate({
|
||||
children: [
|
||||
...localData.children.slice(0, index),
|
||||
{
|
||||
|
@ -76,7 +81,7 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
const appendBlock = (block: BlockData) => {
|
||||
context.emit('update', {
|
||||
props.eventUpdate({
|
||||
children: [
|
||||
...localData.children,
|
||||
block,
|
||||
|
@ -99,14 +104,14 @@ export default defineComponent({
|
|||
return () => (
|
||||
<div class={classes.value}>
|
||||
<SbToolbar slot="toolbar">
|
||||
<button
|
||||
<SbButton
|
||||
type="button"
|
||||
{...{
|
||||
on: {
|
||||
nativeOn: {
|
||||
click: toggleOrientation,
|
||||
},
|
||||
}}
|
||||
>{localData.orientation}</button>
|
||||
>{localData.orientation}</SbButton>
|
||||
</SbToolbar>
|
||||
|
||||
{...localData.children.map((child, index) => (
|
||||
|
|
|
@ -3,6 +3,6 @@ import { getDefaultData } from './util';
|
|||
export default {
|
||||
name: 'sb-layout',
|
||||
getDefaultData,
|
||||
edit: () => import('./edit'),
|
||||
display: () => import('./edit'),
|
||||
edit: () => import('./edit.tsx'),
|
||||
display: () => import('./display.tsx'),
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
.sb-layout {
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
|
||||
&_vertical {
|
||||
flex-direction: column;
|
||||
|
@ -8,4 +9,8 @@
|
|||
&_horizontal {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ export interface LayoutData {
|
|||
|
||||
export interface LayoutProps extends BlockProps {
|
||||
data: LayoutData;
|
||||
eventUpdate: (b?: BlockData) => void;
|
||||
}
|
||||
|
||||
export const getDefaultData: () => LayoutData = () => ({
|
||||
|
|
58
src/components/user/Paragraph/display.tsx
Normal file
58
src/components/user/Paragraph/display.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import {
|
||||
defineComponent,
|
||||
reactive,
|
||||
computed,
|
||||
ref,
|
||||
Ref,
|
||||
onMounted,
|
||||
watch,
|
||||
PropType,
|
||||
} from '@vue/composition-api';
|
||||
import {
|
||||
model,
|
||||
blockProps,
|
||||
useActivation,
|
||||
} from '@components/TreeElement';
|
||||
|
||||
import SbToolbar from '@internal/Toolbar';
|
||||
|
||||
import {
|
||||
getDefaultData,
|
||||
ParagraphData,
|
||||
ParagraphProps,
|
||||
} from './util';
|
||||
|
||||
import './style.scss';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'sb-paragraph-edit',
|
||||
|
||||
model,
|
||||
|
||||
props: {
|
||||
...blockProps,
|
||||
data: {
|
||||
type: (null as unknown) as PropType<ParagraphData>,
|
||||
default: getDefaultData,
|
||||
},
|
||||
eventUpdate: {
|
||||
type: (Function as unknown) as (b?: ParagraphData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
eventInsertBlock: {
|
||||
type: (Function as unknown) as (b?: ParagraphData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props: ParagraphProps, context) {
|
||||
const classes = computed(() => ({
|
||||
'sb-paragraph': true,
|
||||
[`sb-paragraph_align-${props.data.align}`]: true,
|
||||
}));
|
||||
|
||||
return () => (
|
||||
<p class={classes}>{props.data.value}</p>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -35,6 +35,14 @@ export default defineComponent({
|
|||
type: (null as unknown) as PropType<ParagraphData>,
|
||||
default: getDefaultData,
|
||||
},
|
||||
eventUpdate: {
|
||||
type: (Function as unknown) as (b?: ParagraphData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
eventInsertBlock: {
|
||||
type: (Function as unknown) as (b?: ParagraphData) => void,
|
||||
default: () => () => undefined,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props: ParagraphProps, context) {
|
||||
|
@ -81,7 +89,7 @@ export default defineComponent({
|
|||
}));
|
||||
|
||||
const setAlignment = ($event: Event) => {
|
||||
context.emit('update', { align: ($event.target as HTMLSelectElement).value });
|
||||
props.eventUpdate({ align: ($event.target as HTMLSelectElement).value });
|
||||
};
|
||||
|
||||
const onFocus = () => {
|
||||
|
@ -90,7 +98,7 @@ export default defineComponent({
|
|||
|
||||
const onBlur = () => {
|
||||
localData.focused = false;
|
||||
context.emit('update', {
|
||||
props.eventUpdate({
|
||||
value: localData.value,
|
||||
});
|
||||
activate(null);
|
||||
|
@ -99,7 +107,7 @@ export default defineComponent({
|
|||
const onKeypress = ($event: KeyboardEvent) => {
|
||||
if ($event.key === 'Enter') {
|
||||
const blockId = `${+(new Date())}`;
|
||||
context.emit('insert-block', {
|
||||
props.eventInsertBlock({
|
||||
blockId,
|
||||
name: 'sb-paragraph',
|
||||
data: getDefaultData(),
|
||||
|
|
|
@ -4,5 +4,5 @@ export default {
|
|||
name: 'sb-paragraph',
|
||||
getDefaultData,
|
||||
edit: () => import('./edit'),
|
||||
display: () => import('./edit'),
|
||||
display: () => import('./display'),
|
||||
};
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
.sb-paragraph {
|
||||
flex-basis: 100%;
|
||||
|
||||
&__input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
&_align {
|
||||
|
|
3
src/directives/activation-cover.js
Normal file
3
src/directives/activation-cover.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default {
|
||||
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
}
|
||||
|
||||
html {
|
||||
--bg: white;
|
||||
--grey-0: white;
|
||||
--grey-1-t: rgba(0, 0, 0, 0.05);
|
||||
--grey-1: rgb(242, 242, 242);
|
||||
--grey-2-t: rgba(0, 0, 0, 0.1);
|
||||
|
@ -15,4 +15,13 @@ html {
|
|||
--grey-5-t: rgba(0, 0, 0, 0.7);
|
||||
--grey-5: rgb(75, 75, 75);
|
||||
--black: rgba(0, 0, 0, 0.9);
|
||||
|
||||
--bg: var(--grey-1);
|
||||
--fg: var(--black);
|
||||
|
||||
--interact: #3f9cff;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue