diff --git a/packages/core/lib/components/Block.scss b/packages/core/lib/components/Block.scss index 62c4e85..3dc05a5 100644 --- a/packages/core/lib/components/Block.scss +++ b/packages/core/lib/components/Block.scss @@ -5,6 +5,8 @@ align-items: stretch; justify-items: stretch; height: auto; + min-width: 32px; + min-height: 32px; > * > .sb-toolbar { opacity: 0; diff --git a/packages/core/lib/components/BlockPicker.tsx b/packages/core/lib/components/BlockPicker.tsx index b404af7..4b5cdfc 100644 --- a/packages/core/lib/components/BlockPicker.tsx +++ b/packages/core/lib/components/BlockPicker.tsx @@ -25,7 +25,7 @@ export const SbBlockPicker = defineComponent({ const blockList = computed(() => Object.keys(customBlocks).map((key) => customBlocks[key])); - const selectBlock = (block: IBlockDefinition) => () => { + const selectBlock = (block: IBlockDefinition) => { open.value = false; props.onPickedBlock({ name: block.name, diff --git a/packages/core/lib/components/Select.scss b/packages/core/lib/components/Select.scss index de3326b..53ad6f0 100644 --- a/packages/core/lib/components/Select.scss +++ b/packages/core/lib/components/Select.scss @@ -2,6 +2,7 @@ background-color: var(--grey-0); border: 1px solid var(--grey-2); position: relative; + font-size: 1rem; &:hover { border: 1px solid var(--interact); diff --git a/packages/docs/docs/initial-data.json b/packages/docs/docs/initial-data.json index d3cf6b7..c9312f4 100644 --- a/packages/docs/docs/initial-data.json +++ b/packages/docs/docs/initial-data.json @@ -4,6 +4,15 @@ "data": { "orientation": "vertical", "children": [ + { + "name": "sb-heading", + "id": "1480592112212", + "data": { + "value": "A pretty heading", + "align": "center", + "level": 1 + } + }, { "name": "sb-paragraph", "id": "1590592112200", diff --git a/packages/heading/lib/display.ts b/packages/heading/lib/display.ts new file mode 100644 index 0000000..0598f3b --- /dev/null +++ b/packages/heading/lib/display.ts @@ -0,0 +1,44 @@ +import { + defineComponent, + computed, + PropType, + h, +} from 'vue'; +import { + model, +} from '@schlechtenburg/core'; +import { + getDefaultData, + IHeadingData, +} from './util'; + +import './style.scss'; + +export default defineComponent({ + name: 'sb-heading-display', + + model, + + props: { + data: { + type: Object as PropType, + default: getDefaultData, + }, + }, + + setup(props) { + const classes = computed(() => ({ + 'sb-heading': true, + [`sb-heading_align-${props.data.align}`]: true, + [`sb-heading_${props.data.level}`]: true, + })); + + return () => h( + `h${props.data.level}`, + { + class: classes, + innerHTML: props.data.value, + }, + ); + }, +}); diff --git a/packages/heading/lib/edit.tsx b/packages/heading/lib/edit.tsx index ff8b4c5..6f7f7d5 100644 --- a/packages/heading/lib/edit.tsx +++ b/packages/heading/lib/edit.tsx @@ -1 +1,199 @@ -export default {}; +import { + defineComponent, + reactive, + computed, + ref, + Ref, + onMounted, + watch, + PropType, +} from 'vue'; +import { + model, + useActivation, + SbToolbar, + SbSelect, +} from '@schlechtenburg/core'; +import { + getDefaultData, + IHeadingData +} from './util'; +import { getDefaultData as getDefaultParagraphData } from '@schlechtenburg/paragraph'; + +import './style.scss'; + +export default defineComponent({ + name: 'sb-heading-edit', + + model, + + props: { + blockId: { type: String, required: true }, + data: { + type: (null as unknown) as PropType, + default: getDefaultData, + }, + onUpdate: { type: Function, default: () => {} }, + onAppendBlock: { type: Function, default: () => {} }, + onRemoveSelf: { type: Function, default: () => {} }, + onActivateNext: { type: Function, default: () => {} }, + onActivatePrevious: { type: Function, default: () => {} }, + }, + + setup(props) { + const localData = (reactive({ + value: props.data.value, + align: props.data.align, + level: props.data.level, + focused: false, + }) as unknown) as { + value: string; + align: string; + level: number; + focused: boolean; + }; + + const inputEl: Ref = ref(null); + + const { isActive, activate } = useActivation(props.blockId); + + const focusInput = () => { + if (inputEl.value && isActive.value) { + inputEl.value.focus(); + } + }; + + onMounted(() => { + focusInput(); + if (inputEl.value) { + inputEl.value.innerHTML = localData.value; + } + }); + + watch(isActive, focusInput); + + watch(() => props.data, () => { + localData.value = props.data.value; + localData.align = props.data.align; + localData.level = props.data.level; + if (inputEl.value) { + inputEl.value.innerHTML = localData.value; + } + }); + + const onTextUpdate = ($event: Event) => { + localData.value = ($event.target as HTMLElement).innerHTML; + }; + + const classes = computed(() => ({ + 'sb-heading': true, + 'sb-heading_focused': localData.focused, + [`sb-heading_align${localData.align}`]: true, + [`sb-heading_${localData.level}`]: true, + })); + + const setLevel = ($event: Event) => { + props.onUpdate({ + ...localData, + level: parseInt(($event.target as HTMLSelectElement).value, 10), + }); + }; + + const setAlignment = ($event: Event) => { + props.onUpdate({ + ...localData, + align: ($event.target as HTMLSelectElement).value, + }); + }; + + const onFocus = () => { + localData.focused = true; + activate(); + }; + + const onBlur = () => { + localData.focused = false; + props.onUpdate({ + value: localData.value, + align: localData.align, + level: localData.level, + }); + }; + + const onKeydown = ($event: KeyboardEvent) => { + if ($event.key === 'Enter' && !$event.shiftKey) { + const id = `${+(new Date())}`; + props.onAppendBlock({ + id, + name: 'sb-paragraph', + data: getDefaultParagraphData(), + }); + + activate(id); + + $event.preventDefault(); + } + }; + + const onKeyup = ($event: KeyboardEvent) => { + 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 = node ? childNodes.indexOf(node as ChildNode) : -1; + if (node === inputEl.value || index === 0 || index === childNodes.length -1) { + switch ($event.key) { + case 'ArrowDown': + props.onActivateNext(); + break; + case 'ArrowUp': + props.onActivatePrevious(); + break; + } + } + }; + + return () => ( +
+ + + + + + + + + + + + + + + +

+
+ ); + }, +}); diff --git a/packages/heading/lib/index.ts b/packages/heading/lib/index.ts index f27e56c..e527865 100644 --- a/packages/heading/lib/index.ts +++ b/packages/heading/lib/index.ts @@ -8,5 +8,5 @@ export default { name, getDefaultData, edit: defineAsyncComponent(() => import('./edit')), - display: defineAsyncComponent(() => import('./edit')), + display: defineAsyncComponent(() => import('./display')), }; diff --git a/packages/heading/lib/style.scss b/packages/heading/lib/style.scss new file mode 100644 index 0000000..7ba817b --- /dev/null +++ b/packages/heading/lib/style.scss @@ -0,0 +1,39 @@ +.sb-heading { + flex-basis: 100%; + font-weight: bold; + + &_1 { + font-size: 4rem; + } + + &_2 { + font-size: 3rem; + } + + &_3 { + font-size: 2rem; + } + + &_4 { + font-size: 1.6rem; + } + + &_5 { + font-size: 1.2rem; + } + + &_6 { + font-size: 1rem; + } + + &__input { + display: block; + flex-basis: 100%; + } + + &_align { + &-left { &, .sb-heading__input { text-align: left; } } + &-right { &, .sb-heading__input { text-align: right; } } + &-center { &, .sb-heading__input { text-align: center; } } + } +} diff --git a/packages/heading/lib/util.ts b/packages/heading/lib/util.ts index c322598..3a6342d 100644 --- a/packages/heading/lib/util.ts +++ b/packages/heading/lib/util.ts @@ -1,2 +1,11 @@ -export const a = 1; -export const getDefaultData = () => ({}); +export interface IHeadingData { + value: string; + align: string; + level: number; +} + +export const getDefaultData: () => IHeadingData = () => ({ + value: '', + align: 'left', + level: 1, +}); diff --git a/packages/heading/package.json b/packages/heading/package.json index b626ef2..8b3b4f4 100644 --- a/packages/heading/package.json +++ b/packages/heading/package.json @@ -25,6 +25,7 @@ }, "dependencies": { "@schlechtenburg/core": "^0.0.0", + "@schlechtenburg/paragraph": "^0.0.0", "vue": "^3.0.7" } } diff --git a/packages/image/lib/display.tsx b/packages/image/lib/display.tsx index 79fadde..6076105 100644 --- a/packages/image/lib/display.tsx +++ b/packages/image/lib/display.tsx @@ -5,7 +5,7 @@ import { } from '@schlechtenburg/core'; import { getDefaultData, - ImageData, + IImageData, } from './util'; import './style.scss'; @@ -17,7 +17,7 @@ export default defineComponent({ props: { data: { - type: (null as unknown) as PropType, + type: (null as unknown) as PropType, default: getDefaultData, }, }, diff --git a/packages/image/lib/edit.tsx b/packages/image/lib/edit.tsx index 5c5cfb1..d2076d9 100644 --- a/packages/image/lib/edit.tsx +++ b/packages/image/lib/edit.tsx @@ -13,10 +13,10 @@ import { SbBlock, IBlockData, } from '@schlechtenburg/core'; -import { ParagraphData } from '@schlechtenburg/paragraph'; +import { IParagraphData } from '@schlechtenburg/paragraph'; import { getDefaultData, - ImageData, + IImageData, } from './util'; import './style.scss'; @@ -29,7 +29,7 @@ export default defineComponent({ props: { onUpdate: { type: Function, default: () => {} }, data: { - type: (null as unknown) as PropType, + type: (null as unknown) as PropType, default: getDefaultData, }, }, @@ -75,7 +75,7 @@ export default defineComponent({ } }; - const onDescriptionUpdate = (description: IBlockData) => { + const onDescriptionUpdate = (description: IBlockData) => { props.onUpdate({ ...props.data, description, @@ -104,7 +104,7 @@ export default defineComponent({ /> ) => onDescriptionUpdate(updated)} + onUpdate={(updated: IBlockData) => onDescriptionUpdate(updated)} /> : Select Image diff --git a/packages/image/lib/util.ts b/packages/image/lib/util.ts index 039724b..9d15a25 100644 --- a/packages/image/lib/util.ts +++ b/packages/image/lib/util.ts @@ -4,17 +4,17 @@ import { } from '@schlechtenburg/core'; import { name as paragraphName, - ParagraphData, + IParagraphData, getDefaultData as getDefaultParagraphData } from '@schlechtenburg/paragraph'; -export interface ImageData { +export interface IImageData { src: string; alt: string; - description: IBlockData; + description: IBlockData; } -export const getDefaultData: () => ImageData = () => ({ +export const getDefaultData: () => IImageData = () => ({ src: '', alt: '', description: { diff --git a/packages/layout/lib/display.tsx b/packages/layout/lib/display.tsx index 2c41f91..ef04d05 100644 --- a/packages/layout/lib/display.tsx +++ b/packages/layout/lib/display.tsx @@ -8,7 +8,7 @@ import { SbBlock, } from '@schlechtenburg/core'; import { - LayoutData, + ILayoutData, getDefaultData, } from './util'; @@ -21,7 +21,7 @@ export default defineComponent({ props: { data: { - type: (null as unknown) as PropType, + type: (null as unknown) as PropType, default: getDefaultData, }, }, diff --git a/packages/layout/lib/edit.tsx b/packages/layout/lib/edit.tsx index 8fa286f..02937b2 100644 --- a/packages/layout/lib/edit.tsx +++ b/packages/layout/lib/edit.tsx @@ -18,7 +18,7 @@ import { } from '@schlechtenburg/core'; import { - LayoutData, + ILayoutData, getDefaultData, } from './util'; @@ -32,7 +32,7 @@ export default defineComponent({ props: { onUpdate: { type: Function, default: () => {} }, data: { - type: (null as unknown) as PropType, + type: (null as unknown) as PropType, default: getDefaultData, }, }, @@ -40,7 +40,7 @@ export default defineComponent({ setup(props) { const { activate } = useActivation(); - const localData: LayoutData = reactive({ + const localData: ILayoutData = reactive({ orientation: props.data.orientation, children: [...props.data.children], }); @@ -79,6 +79,7 @@ export default defineComponent({ }; const appendBlock = (block: IBlockData) => { + console.log(appendBlock); localData.children = [ ...localData.children, block, diff --git a/packages/layout/lib/util.ts b/packages/layout/lib/util.ts index 4da495a..6847aa3 100644 --- a/packages/layout/lib/util.ts +++ b/packages/layout/lib/util.ts @@ -1,11 +1,11 @@ import { IBlockData } from '@schlechtenburg/core'; -export interface LayoutData { +export interface ILayoutData { orientation: string; children: IBlockData[]; } -export const getDefaultData: () => LayoutData = () => ({ +export const getDefaultData: () => ILayoutData = () => ({ orientation: 'vertical', children: [], }); diff --git a/packages/paragraph/lib/display.tsx b/packages/paragraph/lib/display.tsx index 1e6e3e7..d2b25f0 100644 --- a/packages/paragraph/lib/display.tsx +++ b/packages/paragraph/lib/display.tsx @@ -8,7 +8,7 @@ import { } from '@schlechtenburg/core'; import { getDefaultData, - ParagraphData, + IParagraphData, } from './util'; import './style.scss'; @@ -20,7 +20,7 @@ export default defineComponent({ props: { data: { - type: Object as PropType, + type: Object as PropType, default: getDefaultData, }, }, diff --git a/packages/paragraph/lib/edit.tsx b/packages/paragraph/lib/edit.tsx index 1e139c0..161f163 100644 --- a/packages/paragraph/lib/edit.tsx +++ b/packages/paragraph/lib/edit.tsx @@ -16,7 +16,7 @@ import { } from '@schlechtenburg/core'; import { getDefaultData, - ParagraphData, + IParagraphData, } from './util'; import './style.scss'; @@ -29,7 +29,7 @@ export default defineComponent({ props: { blockId: { type: String, required: true }, data: { - type: (null as unknown) as PropType, + type: (null as unknown) as PropType, default: getDefaultData, }, onUpdate: { type: Function, default: () => {} }, diff --git a/packages/paragraph/lib/util.ts b/packages/paragraph/lib/util.ts index 33e7eaf..0cedfeb 100644 --- a/packages/paragraph/lib/util.ts +++ b/packages/paragraph/lib/util.ts @@ -1,9 +1,9 @@ -export interface ParagraphData { +export interface IParagraphData { value: string; align: string; } -export const getDefaultData: () => ParagraphData = () => ({ +export const getDefaultData: () => IParagraphData = () => ({ value: '', align: 'left', });