diff --git a/packages/core/lib/components/Block.tsx b/packages/core/lib/components/Block.tsx index 10c53bb..a3f49d6 100644 --- a/packages/core/lib/components/Block.tsx +++ b/packages/core/lib/components/Block.tsx @@ -129,7 +129,7 @@ export const SbBlock = defineComponent({ />; } - if (mode.value === SbMode.Display) { + if (mode.value === SbMode.View) { return []; + block: IBlockData; + onUpdate: OnUpdateBlockCb; + mode: SbMode; +} + import './Main.scss'; export const SbMain = defineComponent({ @@ -60,13 +68,18 @@ export const SbMain = defineComponent({ }, }, - setup(props: any) { // TODO: why does the typing of props not work here? + setup(props: ISbMainProps) { const el: Ref = ref(null); useResizeObserver(el, SymEditorDimensions); const mode = ref(props.mode); provide(SymMode, mode); + watch(() => props.mode, (newMode) => { + console.log('Mode update', newMode); + mode.value = newMode; + }); + const activeBlock = ref(null); provide(SymActiveBlock, activeBlock); diff --git a/packages/core/lib/components/MissingBlock/index.ts b/packages/core/lib/components/MissingBlock/index.ts index f484acb..05def6d 100644 --- a/packages/core/lib/components/MissingBlock/index.ts +++ b/packages/core/lib/components/MissingBlock/index.ts @@ -2,6 +2,6 @@ import { defineAsyncComponent } from 'vue'; export default { name: 'sb-missing-block', - edit: defineAsyncComponent(() => import('./display')), - display: defineAsyncComponent(() => import('./display')), + edit: defineAsyncComponent(() => import('./view')), + view: defineAsyncComponent(() => import('./view')), }; diff --git a/packages/core/lib/components/MissingBlock/display.tsx b/packages/core/lib/components/MissingBlock/view.tsx similarity index 100% rename from packages/core/lib/components/MissingBlock/display.tsx rename to packages/core/lib/components/MissingBlock/view.tsx diff --git a/packages/core/lib/mode.ts b/packages/core/lib/mode.ts index 3325c31..26064dc 100644 --- a/packages/core/lib/mode.ts +++ b/packages/core/lib/mode.ts @@ -3,6 +3,6 @@ */ export enum SbMode { Edit = 'edit', - Display = 'display', + View = 'view', } export const SymMode = Symbol('Schlechtenburg mode'); diff --git a/packages/docs/lib/.vitepress/config.ts b/packages/docs/lib/.vitepress/config.ts index 2f74414..eac1f0d 100644 --- a/packages/docs/lib/.vitepress/config.ts +++ b/packages/docs/lib/.vitepress/config.ts @@ -4,8 +4,6 @@ export default defineConfig({ title: 'Schlechtenburg', description: 'Experimental WYSIWYG block editor', - base: '/schlechtenburg/', - themeConfig: { nav: [ { @@ -27,6 +25,7 @@ export default defineConfig({ text: 'Getting Started', children: [ { text: 'Why Schlechtenburg?', link: '/guide/why' }, + { text: 'Examples', link: '/guide/examples' }, { text: 'Installation', link: '/guide/installation' }, ], } diff --git a/packages/docs/lib/ExampleEditor.tsx b/packages/docs/lib/ExampleEditor.tsx index a0708f7..075f073 100644 --- a/packages/docs/lib/ExampleEditor.tsx +++ b/packages/docs/lib/ExampleEditor.tsx @@ -25,37 +25,20 @@ export default defineComponent({ const displayedElement = computed(() => { switch (activeTab.value) { - case SbMode.Edit: - return ) => { - block.data = newBlock.data; - }} - availableBlocks={[ - SbLayout, - SbHeading, - SbImage, - SbParagraph, - ]} - key="edit" - mode={SbMode.Edit} - />; - case SbMode.Display: - return ; case 'data': return
{ JSON.stringify(block, null, 2) }
; + default: + return ; } }); @@ -70,8 +53,8 @@ export default defineComponent({ activeTab.value = ($event.target as HTMLSelectElement).value; }} > - - + + diff --git a/packages/docs/lib/ExampleStandaloneEditor.tsx b/packages/docs/lib/ExampleStandaloneEditor.tsx new file mode 100644 index 0000000..087093d --- /dev/null +++ b/packages/docs/lib/ExampleStandaloneEditor.tsx @@ -0,0 +1,51 @@ +import { + defineComponent, + onMounted, +} from 'vue'; + +import { startSchlechtenburg } from '@schlechtenburg/standalone'; +import { SbMode } from '@schlechtenburg/core'; + +import SbLayout from '@schlechtenburg/layout'; +import SbHeading from '@schlechtenburg/heading'; +import SbParagraph from '@schlechtenburg/paragraph'; +import SbImage from '@schlechtenburg/image'; + +import exampleData from './example-data'; + +import './ExampleEditor.scss'; + +export default defineComponent({ + name: 'ExampleStandaloneEditor', + + setup() { + const block = exampleData; + + onMounted(async () => { + const { getBlock } = await startSchlechtenburg( + '#example-editor', + { + // The input block data + block, + + mode: SbMode.Edit, + // The list of available blocks in this editor instance + availableBlocks: [ + SbLayout, + SbHeading, + SbParagraph, + SbImage, + ], + + // This callback will be alled any time the block data gets updated + onUpdate: (blockData) => { + console.log('Got onUpdate', blockData); + console.log('getBlock', getBlock()); + } + }, + ) + }); + + return () =>
; + }, +}); diff --git a/packages/docs/lib/guide/examples.md b/packages/docs/lib/guide/examples.md new file mode 100644 index 0000000..ce6ebe1 --- /dev/null +++ b/packages/docs/lib/guide/examples.md @@ -0,0 +1,20 @@ + + +# Examples + +## Vue Component without wrapper + +This documentation website already uses Vue under the hood, so Schlechtenburg can just imported as +any other component: + + + +## Wrapped with Vue + +`@schlechtenburg/standalone` gives you a wrapped version of the editor in case you don't have Vue +already installed in your application + + diff --git a/packages/docs/lib/guide/installation.md b/packages/docs/lib/guide/installation.md index 697c0ca..6a1d841 100644 --- a/packages/docs/lib/guide/installation.md +++ b/packages/docs/lib/guide/installation.md @@ -1,6 +1,83 @@ # Installation -First, install the editor core and any blocks you want to use: +Schlechtenburg is very modular; consisting of one core package and multiple blocks. All packages are versioned together, +meaning that v2.0.3 of one package is guaranteed to work with v2.0.3 of another schlechtenburg package. + +Schlechtenburg is basically one Vue component, so if you're already using Vue you can import and use it directly. +Otherwise, there's the standalone version that comes prepackaged with Vue. + +## You're not yet using Vue + +### Install npm packages + +Install the standalone editor and any blocks you want to use: + +```ts +npm i --save @schlechtenburg/standalone \ + @schlechtenburg/layout \ + @schlechtenburg/heading \ + @schlechtenburg/paragraph +``` + +### Initializing the editor + +```ts +// Import the initialization function +import { startSchlechtenburg } from '@schlechtenburg/standalone'; +import { SbMode } from '@schlechtenburg/core'; + +// The following are some Schlechtenburg blocks that +// will be available when editing or viewing +import { + SbLayout, + getDefaultData as getEmptyLayoutBlock, +} from '@schlechtenburg/layout'; +import { SbHeading } from '@schlechtenburg/heading'; +import { SbParagraph } from '@schlechtenburg/paragraph'; +import { SbImage } from '@schlechtenburg/image'; + +// This will be our input state +const emptyLayout = getEmptyLayoutBlock(); + +// This call initializes the Schlechtenburg editor and viewer. +useSchlechtenburg( + // Selector of the element the editor should bind to. + // Can also the an `HTMLElement` reference. + '#editor', + { + // The input block data + block: emptyLayout, + + // Whether Schlechtenburg is in what-you-see (editing) + // or in what-you-get (viewing) + mode: SbMode.Edit, + + // The list of available blocks in this editor instance + availableBlocks: [ + SbLayout, + SbHeading, + SbParagraph, + SbImage, + ], + + // This callback will be alled any time the block data gets updated + onUpdate: (blockData) => { + console.log('Got new block data', blockData); + + } + }, // +) + +``` + +**Note:** You need to provide both a root node + +## You're already using Vue + + +### Install npm packages + +Install the editor core and any blocks you want to use: ``` npm i --save @schlechtenburg/core \ @@ -10,3 +87,33 @@ npm i --save @schlechtenburg/core \ ``` +### Using the editor component + +The following example uses TSX, but `SbMain` is just a Vue component here and can be imported and used just like any other vue component. + +You need to provide a root + +```tsx +// This is the main Schlechtenburg component +import { SbMain } from '@schlechtenburg/core'; + +// The following are some Schlechtenburg blocks that will be available when editing or viewing +import { SbLayout } from '@schlechtenburg/layout'; +import { SbHeading } from '@schlechtenburg/heading'; +import { SbParagraph } from '@schlechtenburg/paragraph'; +import { SbImage } from '@schlechtenburg/image'; + +// In your component +setup () { + // .. + + return () => ; +} +``` diff --git a/packages/docs/lib/guide/why.md b/packages/docs/lib/guide/why.md index 8341ec0..030c271 100644 --- a/packages/docs/lib/guide/why.md +++ b/packages/docs/lib/guide/why.md @@ -48,11 +48,6 @@ experience is great. Schlechtenburg aims to offer a vast library of reusable com variables, and rules for the editing UI. We call this **SBUI**. Complex blocks require complex editing forms and UIs so most of the work goes into creating this UI. A good Design System should help ease the pain. -## SSR Compatible - -Does as it says; drop Schlechtenburg into Nuxt.js, and not just the display mode but also the editor -itself will render on the server. - ## Accessible Toolbars and editing elements are in the correct tab order, **SBUI** elements are all fully @@ -74,6 +69,12 @@ looks like this: }, ``` +The main advantage here is that it enables you to write your own tooling around the format, since +you don't have to deal with HTML or the DOM directly. This also enables really easy subtree rendering, +by just taking that subtree of the JSON and feeding it to a Schlechtenburg instance. if instead of +rendering a full page you'd only want to render the images, you could find all of the `sb-image` nodes +in the tree and rendering them all inside an `sb-layout` block. + ## So why not Gutenberg? Gutenberg is tied heavily into the Wordpress ecosystem, making its inclusion in other sites harder diff --git a/packages/docs/package.json b/packages/docs/package.json index abd743b..82f8c83 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -26,6 +26,7 @@ "serve": "vitepress serve lib" }, "dependencies": { + "@schlechtenburg/standalone": "^0.0.0", "@schlechtenburg/core": "^0.0.0", "@schlechtenburg/heading": "^0.0.0", "@schlechtenburg/image": "^0.0.0", diff --git a/packages/heading/lib/index.ts b/packages/heading/lib/index.ts index e527865..36abf80 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('./display')), + view: defineAsyncComponent(() => import('./view')), }; diff --git a/packages/heading/lib/display.ts b/packages/heading/lib/view.ts similarity index 100% rename from packages/heading/lib/display.ts rename to packages/heading/lib/view.ts diff --git a/packages/image/lib/index.ts b/packages/image/lib/index.ts index fab01f8..f76194d 100644 --- a/packages/image/lib/index.ts +++ b/packages/image/lib/index.ts @@ -8,5 +8,5 @@ export default { name, getDefaultData, edit: defineAsyncComponent(() => import('./edit')), - display: defineAsyncComponent(() => import('./display')), + view: defineAsyncComponent(() => import('./view')), }; diff --git a/packages/image/lib/display.tsx b/packages/image/lib/view.tsx similarity index 100% rename from packages/image/lib/display.tsx rename to packages/image/lib/view.tsx diff --git a/packages/layout/lib/index.ts b/packages/layout/lib/index.ts index 865de82..9fab1f0 100644 --- a/packages/layout/lib/index.ts +++ b/packages/layout/lib/index.ts @@ -8,5 +8,5 @@ export default { name, getDefaultData, edit: defineAsyncComponent(() => import('./edit')), - display: defineAsyncComponent(() => import('./display')), + view: defineAsyncComponent(() => import('./view')), }; diff --git a/packages/layout/lib/display.tsx b/packages/layout/lib/view.tsx similarity index 100% rename from packages/layout/lib/display.tsx rename to packages/layout/lib/view.tsx diff --git a/packages/paragraph/lib/index.ts b/packages/paragraph/lib/index.ts index 36da411..f1b5363 100644 --- a/packages/paragraph/lib/index.ts +++ b/packages/paragraph/lib/index.ts @@ -8,5 +8,5 @@ export default { name, getDefaultData, edit: defineAsyncComponent(() => import('./edit')), - display: defineAsyncComponent(() => import('./display')), + view: defineAsyncComponent(() => import('./view')), }; diff --git a/packages/paragraph/lib/display.tsx b/packages/paragraph/lib/view.tsx similarity index 100% rename from packages/paragraph/lib/display.tsx rename to packages/paragraph/lib/view.tsx diff --git a/packages/standalone/lib/Schlechtenburg.tsx b/packages/standalone/lib/Schlechtenburg.tsx index dc40d8f..c3c43ea 100644 --- a/packages/standalone/lib/Schlechtenburg.tsx +++ b/packages/standalone/lib/Schlechtenburg.tsx @@ -47,8 +47,8 @@ export const Schlechtenburg = defineComponent({ setup(props: ISchlechtenburgProps) { return () => ; diff --git a/packages/standalone/lib/get-wrapper.tsx b/packages/standalone/lib/get-wrapper.tsx new file mode 100644 index 0000000..8b0c8cf --- /dev/null +++ b/packages/standalone/lib/get-wrapper.tsx @@ -0,0 +1,61 @@ +import { + defineComponent, + ref, + PropType, +} from 'vue' +import { + IBlockDefinition, + IBlockData, + SbMain, + SbMode, + OnUpdateBlockCb, +} from '@schlechtenburg/core'; + +/** + * + */ +export default function getWrapper({ + block, + mode, + availableBlocks, +}) { + return defineComponent({ + name: 'SchlechtenburgWrapper', + + props: { + availableBlocks: { + type: Array as PropType[]>, + default: () => [], + }, + block: { + type: Object as PropType>, + required: true, + }, + /** + * Called when the block should be updated. + */ + onUpdate: { + type: (null as unknown) as PropType, + default: () => {}, + }, + mode: { + type: String as PropType, + validator(value: any) { + return Object.values(SbMode).includes(value); + }, + default: SbMode.Edit, + }, + }, + + setup(props) { + const refBlock = ref({ ...block }); + const refMode = ref({ ...block }); + + return () => + } + }); +} diff --git a/packages/standalone/lib/main.ts b/packages/standalone/lib/main.ts index 66de026..f87d53a 100644 --- a/packages/standalone/lib/main.ts +++ b/packages/standalone/lib/main.ts @@ -1,8 +1,11 @@ -import { createApp } from 'vue' import { - ISchlechtenburgProps, - Schlechtenburg, -} from './Schlechtenburg'; + createApp, +} from 'vue' +import { + ISbMainProps, + IBlockData, + SbMain, +} from '@schlechtenburg/core'; /** * @@ -16,10 +19,21 @@ export const startSchlechtenburg = async ( /** * The schlechtenburg props */ - props:ISchlechtenburgProps, + props:ISbMainProps, ) => { - const app = createApp(Schlechtenburg, props as unknown as Record); + let block = { ...props.block }; + const app = createApp(SbMain, { + ...props, + onUpdate: (update: IBlockData) => { + block = update; + props.onUpdate(update); + }, + }as unknown as Record); app.mount(el); - return app; + return { + getBlock() { + return block; + }, + }; } diff --git a/packages/standalone/package-lock.json b/packages/standalone/package-lock.json index e56c2da..79ff896 100644 --- a/packages/standalone/package-lock.json +++ b/packages/standalone/package-lock.json @@ -1,5 +1,5 @@ { - "name": "@schlechtenburg/core", + "name": "@schlechtenburg/standalone", "version": "0.0.0", "lockfileVersion": 1, "requires": true, @@ -982,11 +982,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1213,7 +1208,8 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true }, "vscode-languageserver-textdocument": { "version": "1.0.4",