Less broken docs

This commit is contained in:
Benjamin Bädorf 2022-03-17 18:59:51 +01:00
parent c5109dba75
commit c3ef4f7f87
No known key found for this signature in database
GPG key ID: 4406E80E13CD656C
66 changed files with 2042 additions and 205 deletions

2
.gitignore vendored
View file

@ -1,8 +1,6 @@
.direnv .direnv
node_modules node_modules
.DS_Store .DS_Store
dist
dist-ssr
*.local *.local
tags tags
.temp .temp

1
docs Symbolic link
View file

@ -0,0 +1 @@
packages/docs/lib/.vitepress/dist

5
package-lock.json generated
View file

@ -4474,6 +4474,11 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true "dev": true
}, },
"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=="
},
"lodash._reinterpolate": { "lodash._reinterpolate": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",

View file

@ -5,5 +5,8 @@
"scripts": {}, "scripts": {},
"devDependencies": { "devDependencies": {
"lerna": "^3.22.1" "lerna": "^3.22.1"
},
"dependencies": {
"lodash-es": "^4.17.21"
} }
} }

View file

@ -1,4 +1,4 @@
import { debounce } from 'lodash-es'; import debounce from 'lodash/debounce';
import { import {
watch, watch,
reactive, reactive,

View file

@ -0,0 +1,31 @@
.sb-main {
position: relative;
background-color: var(--bg);
--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);
--grey-2: rgb(230, 230, 230);
--grey-3-t: rgba(0, 0, 0, 0.2);
--grey-3: rgb(205, 205, 205);
--grey-4-t: rgba(0, 0, 0, 0.4);
--grey-4: rgb(155, 155, 155);
--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;
--interact-lite: #3f9cff;
--z-context-menu: 3000;
*,
*::before,
*::after {
box-sizing: border-box;
}
}

View file

@ -11,6 +11,7 @@ import {
IBlockDefinition, IBlockDefinition,
IBlockLibrary, IBlockLibrary,
ITreeNode, ITreeNode,
OnUpdateBlockCb,
} from '../types'; } from '../types';
import { model } from '../block-helpers'; import { model } from '../block-helpers';
import { SymMode, SbMode } from '../mode'; import { SymMode, SbMode } from '../mode';
@ -27,7 +28,7 @@ import { SbMainMenu } from './MainMenu';
import { SbBlockToolbar } from './BlockToolbar'; import { SbBlockToolbar } from './BlockToolbar';
import { SbBlock } from './Block'; import { SbBlock } from './Block';
import './Schlechtenburg.scss'; import './Main.scss';
export const SbMain = defineComponent({ export const SbMain = defineComponent({
name: 'sb-main', name: 'sb-main',
@ -43,7 +44,13 @@ export const SbMain = defineComponent({
type: Object as PropType<IBlockData<any>>, type: Object as PropType<IBlockData<any>>,
required: true, required: true,
}, },
onUpdate: { type: Function, default: () => {} }, /**
* Called when the block should be updated.
*/
onUpdate: {
type: (null as unknown) as PropType<OnUpdateBlockCb>,
default: () => {},
},
mode: { mode: {
type: String as PropType<SbMode>, type: String as PropType<SbMode>,
validator(value: any) { validator(value: any) {

View file

@ -1,4 +0,0 @@
.sb-main {
position: relative;
background-color: var(--bg);
}

View file

@ -1,4 +1,4 @@
import { debounce } from 'lodash-es'; import debounce from 'lodash/debounce';
import { import {
defineComponent, defineComponent,
watch, watch,

View file

@ -7,7 +7,7 @@ export * from './use-activation';
export * from './use-dynamic-blocks'; export * from './use-dynamic-blocks';
export * from './use-resize-observer'; export * from './use-resize-observer';
export * from './components/Schlechtenburg'; export * from './components/Main';
export * from './components/Block'; export * from './components/Block';
export * from './components/BlockPicker'; export * from './components/BlockPicker';
export * from './components/BlockOrdering'; export * from './components/BlockOrdering';

View file

@ -2,6 +2,7 @@ import {
Ref, Ref,
ref, ref,
inject, inject,
onBeforeMount,
watch, watch,
provide, provide,
} from 'vue'; } from 'vue';
@ -32,16 +33,18 @@ export function useResizeObserver(el: Ref<null|HTMLElement>, symbol: symbol) {
}; };
}; };
const resizeObserver = new ResizeObserver(triggerSizeCalculation); onBeforeMount(() => {
const mutationObserver = new MutationObserver(triggerSizeCalculation); const resizeObserver = new ResizeObserver(triggerSizeCalculation);
const mutationObserver = new MutationObserver(triggerSizeCalculation);
watch(el, () => { watch(el, () => {
if (!el.value) { if (!el.value) {
return; return;
} }
resizeObserver.observe(el.value); resizeObserver.observe(el.value);
mutationObserver.observe(el.value, { attributes: true, childList: false, subtree: false }); mutationObserver.observe(el.value, { attributes: true, childList: false, subtree: false });
}); });
})
return { triggerSizeCalculation, dimensions }; return { triggerSizeCalculation, dimensions };
} }

View file

@ -26,16 +26,17 @@
"url": "git@git.b12f.io:b12f/schlechtenburg.git" "url": "git@git.b12f.io:b12f/schlechtenburg.git"
}, },
"dependencies": { "dependencies": {
"@schlechtenburg/docgen": "^0.0.0", "lodash": "^4.17.21",
"lodash-es": "^4.17.21", "uuid": "^8.3.2"
"uuid": "^8.3.2", },
"peerDependencies": {
"vue": "^3.0.7" "vue": "^3.0.7"
}, },
"devDependencies": { "devDependencies": {
"@vuedx/typecheck": "^0.6.3",
"@vuedx/typescript-plugin-vue": "^0.6.3",
"@schlechtenburg/docgen": "^0.0.0", "@schlechtenburg/docgen": "^0.0.0",
"@types/lodash-es": "^4.17.4", "@types/lodash-es": "^4.17.4",
"@types/uuid": "^8.3.0" "@types/uuid": "^8.3.0",
"@vuedx/typecheck": "^0.6.3",
"@vuedx/typescript-plugin-vue": "^0.6.3"
} }
} }

View file

@ -24,7 +24,8 @@ export default defineConfig({
{ {
text: 'Getting Started', text: 'Getting Started',
children: [ children: [
{ text: 'Introduction', link: '/guide/introduction' } { text: 'Why Schlechtenburg?', link: '/guide/why' },
{ text: 'Installation', link: '/guide/installation' },
], ],
} }
], ],
@ -32,10 +33,11 @@ export default defineConfig({
{ {
text: 'API', text: 'API',
children: [ children: [
{ text: 'Core', link: '/api/core' } { text: 'Core', link: '/api/core' },
], ],
} }
], ],
'/': false,
}, },
}, },
}); });

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
import{m as o,g as s,b as r}from"../index.md.5cef284c.js";import"./style.b4010a05.js";import{k as u,e as d,l as e}from"../app.2b3cbed9.js";var i=u({name:"sb-layout-display",model:o,props:{data:{type:null,default:s}},setup(a){const l=d(()=>({"sb-layout":!0,[`sb-layout_${a.data.orientation}`]:!0}));return()=>e("div",{class:l.value},[...a.data.children.map(t=>e(r,{key:t.id,block:t},null))])}});export{i as default};

View file

@ -0,0 +1 @@
import{m as t,e as s}from"../index.md.5cef284c.js";import"./style.b4010a052.js";import{k as d,e as l,x as n}from"../app.2b3cbed9.js";var m=d({name:"sb-heading-display",model:t,props:{data:{type:Object,default:s}},setup(a){const e=l(()=>({"sb-heading":!0,[`sb-heading_align-${a.data.align}`]:!0,[`sb-heading_${a.data.level}`]:!0}));return()=>n(`h${a.data.level}`,{class:e.value,innerHTML:a.data.value})}});export{m as default};

View file

@ -0,0 +1 @@
import{m as e,j as l,b as s}from"../index.md.5cef284c.js";import"./style.b4010a054.js";import{k as i,l as t}from"../app.2b3cbed9.js";var d=i({name:"sb-image-display",model:e,props:{data:{type:null,default:l}},setup(a){return()=>t("figure",{class:"sb-image"},[t("img",{class:"sb-image__content",src:a.data.src,alt:a.data.alt},null),t(s,{block:a.data.description},null)])}});export{d as default};

View file

@ -0,0 +1 @@
import{m as t,i as r}from"../index.md.5cef284c.js";import"./style.b4010a053.js";import{k as s,e as p,l,m as o}from"../app.2b3cbed9.js";var u=s({name:"sb-paragraph-display",model:t,props:{data:{type:Object,default:r}},setup(a){const e=p(()=>({"sb-paragraph":!0,[`sb-paragraph_align-${a.data.align}`]:!0}));return()=>l("p",o({class:e.value},{innerHTML:a.data.value}),null)}});export{u as default};

View file

@ -0,0 +1 @@
var p=Object.defineProperty,i=Object.defineProperties;var d=Object.getOwnPropertyDescriptors;var n=Object.getOwnPropertySymbols;var r=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable;var o=(t,e,a)=>e in t?p(t,e,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[e]=a,s=(t,e)=>{for(var a in e||(e={}))r.call(e,a)&&o(t,a,e[a]);if(n)for(var a of n(e))m.call(e,a)&&o(t,a,e[a]);return t},l=(t,e)=>i(t,d(e));import{m as c,k as u}from"../index.md.5cef284c.js";import{k as f,l as k,b}from"../app.2b3cbed9.js";var F=f({name:"sb-missing-block",model:c,props:l(s({},u),{name:String,data:{type:null,default:null},eventUpdate:{type:Function,default:()=>{}},eventAppendBlock:{type:Function,default:()=>{}},eventRemoveBlock:{type:Function,default:()=>{}}}),setup(t){return()=>k("div",{class:"sb-missing-block"},[b("Missing block: "),t.name])}});export{F as default};

View file

@ -0,0 +1 @@
var I=Object.defineProperty,T=Object.defineProperties;var U=Object.getOwnPropertyDescriptors;var p=Object.getOwnPropertySymbols;var K=Object.prototype.hasOwnProperty,_=Object.prototype.propertyIsEnumerable;var b=(a,e,t)=>e in a?I(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t,d=(a,e)=>{for(var t in e||(e={}))K.call(e,t)&&b(a,t,e[t]);if(p)for(var t of p(e))_.call(e,t)&&b(a,t,e[t]);return a},s=(a,e)=>T(a,U(e));import{m as L,e as M,u as C,S as H,f as y,h as F,i as P}from"../index.md.5cef284c.js";import"./style.b4010a052.js";import{k as R,g as V,r as q,v as O,w as m,e as j,l,b as o}from"../app.2b3cbed9.js";var Q=R({name:"sb-heading-edit",model:L,props:{blockId:{type:String,required:!0},data:{type:null,default:M},onUpdate:{type:null,default:()=>{}},onAppendBlock:{type:null,default:()=>{}},onRemoveSelf:{type:null,default:()=>{}},onActivateNext:{type:null,default:()=>{}},onActivatePrevious:{type:null,default:()=>{}}},setup(a){const e=V({value:a.data.value,align:a.data.align,level:a.data.level,focused:!1}),t=q(null),{isActive:c,activate:v}=C(a.blockId),f=()=>{t.value&&c.value&&t.value.focus()};O(()=>{f(),t.value&&(t.value.innerHTML=e.value)}),m(c,f),m(()=>a.data,()=>{e.value=a.data.value,e.align=a.data.align,e.level=a.data.level,t.value&&(t.value.innerHTML=e.value)});const k=n=>{e.value=n.target.innerHTML},A=j(()=>({"sb-heading":!0,"sb-heading_focused":e.focused,[`sb-heading_align-${e.align}`]:!0,[`sb-heading_${e.level}`]:!0})),w=n=>{a.onUpdate(s(d({},e),{level:parseInt(n.target.value,10)}))},S=n=>{a.onUpdate(s(d({},e),{align:n.target.value}))},x=()=>{e.focused=!0,v()},D=()=>{e.focused=!1,a.onUpdate({value:e.value,align:e.align,level:e.level})},N=n=>{if(n.key==="Enter"&&!n.shiftKey){const u=F();a.onAppendBlock({id:u,name:"sb-paragraph",data:P()}),v(u),n.preventDefault()}},B=n=>{var h;n.key==="Backspace"&&e.value===""&&a.onRemoveSelf();const u=window.getSelection(),i=u==null?void 0:u.focusNode,r=Array.from(((h=t==null?void 0:t.value)==null?void 0:h.childNodes)||[]),g=i?r.indexOf(i):-1;if(i===t.value||g===0||g===r.length-1)switch(n.key){case"ArrowDown":a.onActivateNext();break;case"ArrowUp":a.onActivatePrevious();break}};return()=>l("div",{class:A.value},[l(H,null,{default:()=>[l(y,{value:e.level,onChange:w},{default:()=>[l("option",{value:1},[o("h1")]),l("option",{value:2},[o("h2")]),l("option",{value:3},[o("h3")]),l("option",{value:4},[o("h4")]),l("option",{value:5},[o("h5")]),l("option",{value:6},[o("h6")])]}),l(y,{value:e.align,onChange:S},{default:()=>[l("option",null,[o("left")]),l("option",null,[o("center")]),l("option",null,[o("right")])]})]}),l("p",{class:"sb-heading__input",ref:t,contenteditable:!0,onInput:k,onFocus:x,onBlur:D,onKeydown:N,onKeyup:B},null)])}});export{Q as default};

View file

@ -0,0 +1 @@
import{m as S,i as g,u as x,S as N,f as B,h as T}from"../index.md.5cef284c.js";import"./style.b4010a053.js";import{k as D,g as I,r as K,v as U,w as p,e as M,l,b as s}from"../app.2b3cbed9.js";var C=D({name:"sb-paragraph-edit",model:S,props:{blockId:{type:String,required:!0},data:{type:null,default:g},onUpdate:{type:null,default:()=>{}},onAppendBlock:{type:null,default:()=>{}},onRemoveSelf:{type:null,default:()=>{}},onActivateNext:{type:null,default:()=>{}},onActivatePrevious:{type:null,default:()=>{}}},setup(a){const e=I({value:a.data.value,align:a.data.align,focused:!1}),t=K(null),{isActive:c,activate:i}=x(a.blockId),d=()=>{t.value&&c.value&&t.value.focus()};U(()=>{d(),t.value&&(t.value.innerHTML=e.value)}),p(c,d),p(()=>a.data,()=>{e.value=a.data.value,e.align=a.data.align,t.value&&(t.value.innerHTML=e.value)});const y=n=>{e.value=n.target.innerHTML},b=M(()=>({"sb-paragraph":!0,"sb-paragraph_focused":e.focused,[`sb-paragraph_align-${e.align}`]:!0})),h=n=>{a.onUpdate({value:e.value,align:n.target.value})},m=()=>{e.focused=!0,i()},k=()=>{e.focused=!1,a.onUpdate({value:e.value,align:e.align})},A=n=>{if(n.key==="Enter"&&!n.shiftKey){const o=T();a.onAppendBlock({id:o,name:"sb-paragraph",data:g()}),i(o),n.preventDefault()}},w=n=>{var v;n.key==="Backspace"&&e.value===""&&a.onRemoveSelf();const o=window.getSelection(),u=o==null?void 0:o.focusNode,r=Array.from(((v=t==null?void 0:t.value)==null?void 0:v.childNodes)||[]),f=u?r.indexOf(u):-1;if(u===t.value||f===0||f===r.length-1)switch(n.key){case"ArrowDown":a.onActivateNext();break;case"ArrowUp":a.onActivatePrevious();break}};return()=>l("div",{class:b.value},[l(N,null,{default:()=>[l(B,{value:e.align,onChange:h},{default:()=>[l("option",null,[s("left")]),l("option",null,[s("center")]),l("option",null,[s("right")])]})]}),l("p",{class:"sb-paragraph__input",ref:t,contenteditable:!0,onInput:y,onFocus:m,onBlur:k,onKeydown:A,onKeyup:w},null)])}});export{C as default};

View file

@ -0,0 +1 @@
var v=Object.defineProperty,S=Object.defineProperties;var k=Object.getOwnPropertyDescriptors;var d=Object.getOwnPropertySymbols;var p=Object.prototype.hasOwnProperty,I=Object.prototype.propertyIsEnumerable;var o=(a,t,e)=>t in a?v(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e,r=(a,t)=>{for(var e in t||(t={}))p.call(t,e)&&o(a,e,t[e]);if(d)for(var e of d(t))I.call(t,e)&&o(a,e,t[e]);return a},u=(a,t)=>S(a,k(t));import{m as U,j as w,S as y,a as f,b as D}from"../index.md.5cef284c.js";import"./style.b4010a054.js";import{k as C,g as h,r as F,w as x,l as n,b as m,F as B}from"../app.2b3cbed9.js";var T=C({name:"sb-image-edit",model:U,props:{onUpdate:{type:null,default:()=>{}},data:{type:null,default:w}},setup(a){const t=h({src:a.data.src,alt:a.data.alt,description:a.data.description}),e=F(null);x(()=>a.data,()=>{t.src=a.data.src,t.alt=a.data.alt,t.description=a.data.description});const i=()=>{e.value&&e.value.click()},g=()=>{if(e.value&&e.value.files&&e.value.files.length){const l=new FileReader;l.addEventListener("load",()=>{var c;const s=(c=l==null?void 0:l.result)==null?void 0:c.toString();if(!s)throw new Error("Couldn't load image src");a.onUpdate({src:s,alt:a.data.alt,description:a.data.description})}),l.readAsDataURL(e.value.files[0])}},b=l=>{a.onUpdate(u(r({},a.data),{description:l}))};return()=>n("figure",{class:"sb-image"},[n(y,null,{default:()=>[t.src?n(f,{onClick:i},{default:()=>[m("Select Image")]}):null,n("input",{type:"file",ref:e,style:"display: none;",onInput:g},null)]}),t.src?n(B,null,[n("img",{src:t.src,alt:t.alt,class:"sb-image__content"},null),n(D,{block:t.description,onUpdate:l=>b(l)},null)]):n(f,{onClick:i},{default:()=>[m("Select Image")]})])}});export{T as default};

View file

@ -0,0 +1 @@
var g=Object.defineProperty;var h=Object.getOwnPropertySymbols;var U=Object.prototype.hasOwnProperty,p=Object.prototype.propertyIsEnumerable;var u=(a,c,e)=>c in a?g(a,c,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[c]=e,r=(a,c)=>{for(var e in c||(c={}))U.call(c,e)&&u(a,e,c[e]);if(h)for(var e of h(c))p.call(c,e)&&u(a,e,c[e]);return a};import{m as w,g as y,u as S,S as A,a as M,b as F,c as P,d as C}from"../index.md.5cef284c.js";import"./style.b4010a05.js";import{k as D,g as I,w as O,e as N,l as o,F as R,m as z}from"../app.2b3cbed9.js";var j=D({name:"sb-layout-edit",model:w,props:{onUpdate:{type:null,default:()=>{}},data:{type:null,default:y}},setup(a){const{activate:c}=S(),e=I({orientation:a.data.orientation,children:[...a.data.children]});O(()=>a.data,()=>{e.orientation=a.data.orientation,e.children=[...a.data.children]});const m=N(()=>({"sb-layout":!0,[`sb-layout_${e.orientation}`]:!0})),v=()=>{a.onUpdate({orientation:e.orientation==="vertical"?"horizontal":"vertical"})},f=(t,n)=>{const l=e.children.indexOf(t);l!==-1&&a.onUpdate({children:[...e.children.slice(0,l),r(r({},t),n),...e.children.slice(l+1)]})},k=t=>{e.children=[...e.children,t],a.onUpdate({children:[...e.children]}),c(t.id)},i=(t,n)=>{e.children=[...e.children.slice(0,t+1),n,...e.children.slice(t+1)],a.onUpdate({children:[...e.children]}),c(n.id)},d=t=>{e.children=[...e.children.slice(0,t),...e.children.slice(t+1)],a.onUpdate({children:[...e.children]});const n=Math.max(t-1,0);c(e.children[n].id)},s=t=>{const n=Math.max(Math.min(e.children.length-1,t),0);c(e.children[n].id)},B=t=>{if(t===0)return;const n=e.children[t],l=e.children[t-1];e.children=[...e.children.slice(0,t-1),n,l,...e.children.slice(t+1)],a.onUpdate({children:[...e.children]})},b=t=>{if(t===e.children.length-1)return;const n=e.children[t],l=e.children[t+1];e.children=[...e.children.slice(0,t),l,n,...e.children.slice(t+2)],a.onUpdate({children:[...e.children]})};return()=>o("div",{class:m.value},[o(A,null,{default:()=>[o(M,{type:"button",onClick:v},{default:()=>[e.orientation]})]}),o(R,null,[...e.children.map((t,n)=>o(F,z({key:t.id},{"data-order":n,block:t,onUpdate:l=>f(t,l),onRemoveSelf:()=>d(n),onPrependBlock:l=>i(n-1,l),onAppendBlock:l=>i(n,l),onActivatePrevious:()=>s(n-1),onActivateNext:()=>s(n+1)}),{"context-toolbar":()=>o(P,{onMoveBackward:()=>B(n),onMoveForward:()=>b(n),onRemove:()=>d(n),orientation:e.orientation},null)}))]),o(C,{onInsertBlock:k},null)])}});export{j as default};

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,5 @@
import{_ as t,c as a,o as n,a as e,b as s}from"./app.2b3cbed9.js";const m='{"title":"Installation","description":"","frontmatter":{},"headers":[],"relativePath":"guide/installation.md"}',o={},l=e("h1",{id:"installation",tabindex:"-1"},[s("Installation "),e("a",{class:"header-anchor",href:"#installation","aria-hidden":"true"},"#")],-1),r=e("p",null,"First, install the editor core and any blocks you want to use:",-1),c=e("div",{class:"language-"},[e("pre",null,[e("code",null,`npm i --save @schlechtenburg/core \\
@schlechtenburg/layout \\
@schlechtenburg/heading \\
@schlechtenburg/paragraph
`)])],-1),i=[l,r,c];function d(h,_,u,p,g,f){return n(),a("div",null,i)}var v=t(o,[["render",d]]);export{m as __pageData,v as default};

View file

@ -0,0 +1,5 @@
import{_ as t,c as a,o as n,a as e,b as s}from"./app.2b3cbed9.js";const m='{"title":"Installation","description":"","frontmatter":{},"headers":[],"relativePath":"guide/installation.md"}',o={},l=e("h1",{id:"installation",tabindex:"-1"},[s("Installation "),e("a",{class:"header-anchor",href:"#installation","aria-hidden":"true"},"#")],-1),r=e("p",null,"First, install the editor core and any blocks you want to use:",-1),c=e("div",{class:"language-"},[e("pre",null,[e("code",null,`npm i --save @schlechtenburg/core \\
@schlechtenburg/layout \\
@schlechtenburg/heading \\
@schlechtenburg/paragraph
`)])],-1),i=[l,r,c];function d(h,_,u,p,g,f){return n(),a("div",null,i)}var v=t(o,[["render",d]]);export{m as __pageData,v as default};

View file

@ -0,0 +1 @@
import{_ as e,c as o,o as a,a as t,b as n}from"./app.2b3cbed9.js";const m='{"title":"Introduction","description":"","frontmatter":{},"headers":[],"relativePath":"guide/introduction.md"}',r={},c=t("h1",{id:"introduction",tabindex:"-1"},[n("Introduction "),t("a",{class:"header-anchor",href:"#introduction","aria-hidden":"true"},"#")],-1),i=[c];function d(s,_,u,h,p,l){return a(),o("div",null,i)}var x=e(r,[["render",d]]);export{m as __pageData,x as default};

View file

@ -0,0 +1 @@
import{_ as e,c as o,o as a,a as t,b as n}from"./app.2b3cbed9.js";const m='{"title":"Introduction","description":"","frontmatter":{},"headers":[],"relativePath":"guide/introduction.md"}',r={},c=t("h1",{id:"introduction",tabindex:"-1"},[n("Introduction "),t("a",{class:"header-anchor",href:"#introduction","aria-hidden":"true"},"#")],-1),i=[c];function d(s,_,u,h,p,l){return a(),o("div",null,i)}var x=e(r,[["render",d]]);export{m as __pageData,x as default};

View file

@ -0,0 +1,9 @@
import{_ as e,c as t,o as a,d as i}from"./app.2b3cbed9.js";const b='{"title":"Why Schlechtenburg?","description":"","frontmatter":{},"headers":[{"level":2,"title":"Block based","slug":"block-based"},{"level":2,"title":"Design system with standardized components","slug":"design-system-with-standardized-components"},{"level":2,"title":"SSR Compatible","slug":"ssr-compatible"},{"level":2,"title":"Accessible","slug":"accessible"},{"level":2,"title":"JSON only","slug":"json-only"},{"level":2,"title":"So why not Gutenberg?","slug":"so-why-not-gutenberg"}],"relativePath":"guide/why.md"}',r={},o=i(`<h1 id="why-schlechtenburg" tabindex="-1">Why Schlechtenburg? <a class="header-anchor" href="#why-schlechtenburg" aria-hidden="true">#</a></h1><p>Installing a WYSIWYG editor in your application or on your website is often the easy part. The hard part comes afterwards: extending and customizing the editor to fit your specific needs. There are currently a couple of very good editors, but after reading this text I think you&#39;ll agree there&#39;s still room for improvement. We&#39;ll be looking at the following alternative WYSIWYG editors:</p><ul><li><a href="https://quilljs.com/" target="_blank" rel="noopener noreferrer">Quill</a></li><li><a href="https://ckeditor.com/ckeditor-5/" target="_blank" rel="noopener noreferrer">CKEditor</a></li><li><a href="https://wordpress.org/gutenberg/" target="_blank" rel="noopener noreferrer">Gutenberg</a></li></ul><h2 id="block-based" tabindex="-1">Block based <a class="header-anchor" href="#block-based" aria-hidden="true">#</a></h2><p>Though all three are meant for text editing, Quill and CKEditor are a bit more explicit about this in their architecture:</p><ul><li>They input and output a string</li><li>They have one global toolbar</li></ul><p>Gutenberg is a bit more involved, literally using building &quot;blocks&quot; to create it&#39;s editor. Instead of seeing the content as a long string it takes a more component-esque approach. For example, the following things are all their own blocks in the gutenberg editor, which a specific react component that handles the editing mode, and one that handles the display mode.</p><ul><li>Paragraph</li><li>Heading</li><li>List</li><li>Image</li><li>Column-based layout</li><li>External media embeds like YouTube videos</li></ul><p>When editing, you are editing, adding or removing one specific block at a time. Blocks can contain other blocks in a tree structure, and they all have their own (but uniform) editing UI.</p><p>Schlechtenburg takes a block based approach, just like its namesake Gutenberg. This has a couple of advantages:</p><ul><li>You can take strong control over the final rendered HTML of a block that you create</li><li>Blocks are easily published as reusable JS modules</li><li>You very rarely need blocks inside text, but the reverse has abundant usecases</li><li>The mental model is closer to how actual HTML works</li><li>Very complex pages can be handled by the editor</li><li>If you know React or Vue, you understand a lot about how to write blocks for the editor</li></ul><h2 id="design-system-with-standardized-components" tabindex="-1">Design system with standardized components <a class="header-anchor" href="#design-system-with-standardized-components" aria-hidden="true">#</a></h2><p>Making sure the final rendered data looks correct is always easier than making sure the editing experience is great. Schlechtenburg aims to offer a vast library of reusable components, patterns, variables, and rules for the editing UI. We call this <strong>SBUI</strong>. 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.</p><h2 id="ssr-compatible" tabindex="-1">SSR Compatible <a class="header-anchor" href="#ssr-compatible" aria-hidden="true">#</a></h2><p>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.</p><h2 id="accessible" tabindex="-1">Accessible <a class="header-anchor" href="#accessible" aria-hidden="true">#</a></h2><p>Toolbars and editing elements are in the correct tab order, <strong>SBUI</strong> elements are all fully accessible.</p><h2 id="json-only" tabindex="-1">JSON only <a class="header-anchor" href="#json-only" aria-hidden="true">#</a></h2><p>Input and Output is one standardized, typed, JSON-stringifyable object. For example, a paragraph looks like this:</p><div class="language-"><pre><code>{
id: &#39;1590592116800&#39;,
name: &#39;sb-paragraph&#39;,
data: {
value: &#39;This is the second paragraph&lt;br&gt;&#39;,
align: &#39;left&#39;
}
},
</code></pre></div><h2 id="so-why-not-gutenberg" tabindex="-1">So why not Gutenberg? <a class="header-anchor" href="#so-why-not-gutenberg" aria-hidden="true">#</a></h2><p>Gutenberg is tied heavily into the Wordpress ecosystem, making its inclusion in other sites harder than necessary.</p>`,22),n=[o];function s(l,d,h,c,u,g){return a(),t("div",null,n)}var m=e(r,[["render",s]]);export{b as __pageData,m as default};

View file

@ -0,0 +1 @@
import{_ as e,c as t,o as a,d as i}from"./app.2b3cbed9.js";const b='{"title":"Why Schlechtenburg?","description":"","frontmatter":{},"headers":[{"level":2,"title":"Block based","slug":"block-based"},{"level":2,"title":"Design system with standardized components","slug":"design-system-with-standardized-components"},{"level":2,"title":"SSR Compatible","slug":"ssr-compatible"},{"level":2,"title":"Accessible","slug":"accessible"},{"level":2,"title":"JSON only","slug":"json-only"},{"level":2,"title":"So why not Gutenberg?","slug":"so-why-not-gutenberg"}],"relativePath":"guide/why.md"}',r={},o=i("",22),n=[o];function s(l,d,h,c,u,g){return a(),t("div",null,n)}var m=e(r,[["render",s]]);export{b as __pageData,m as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Installation | Schlechtenburg</title>
<meta name="description" content="Experimental WYSIWYG block editor">
<link rel="stylesheet" href="/assets/style.a7ab32f9.css">
<link rel="modulepreload" href="/assets/app.2b3cbed9.js">
<link rel="modulepreload" href="/assets/guide_installation.md.7ba80841.lean.js">
<meta name="twitter:title" content="Installation | Schlechtenburg">
<meta property="og:title" content="Installation | Schlechtenburg">
</head>
<body>
<div id="app"><!--[--><div class="theme"><header class="nav-bar" data-v-675d8756><div class="sidebar-button" data-v-675d8756><svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class></path></svg></div><a class="nav-bar-title" href="/" aria-label="Schlechtenburg, back to home" data-v-675d8756 data-v-cc01ef16><!----> Schlechtenburg</a><div class="flex-grow" data-v-675d8756></div><div class="nav" data-v-675d8756><nav class="nav-links" data-v-675d8756 data-v-eab3edfe><!--[--><div class="item" data-v-eab3edfe><div class="nav-link" data-v-eab3edfe data-v-b8818f8c><a class="item active" href="/guide/introduction" data-v-b8818f8c>Guide <!----></a></div></div><div class="item" data-v-eab3edfe><div class="nav-dropdown-link" data-v-eab3edfe data-v-56bf3a3f><button class="button" data-v-56bf3a3f><span class="button-text" data-v-56bf3a3f>API</span><span class="right button-arrow" data-v-56bf3a3f></span></button><ul class="dialog" data-v-56bf3a3f><!--[--><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/api/@schlechtenburg/core" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>@schlechtenburg/core</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><!--]--></ul></div></div><!--]--><!----><!----></nav></div><!--[--><!--]--></header><aside class="sidebar" data-v-83e92a68><nav class="nav-links nav" data-v-83e92a68 data-v-eab3edfe><!--[--><div class="item" data-v-eab3edfe><div class="nav-link" data-v-eab3edfe data-v-b8818f8c><a class="item active" href="/guide/introduction" data-v-b8818f8c>Guide <!----></a></div></div><div class="item" data-v-eab3edfe><div class="nav-dropdown-link" data-v-eab3edfe data-v-56bf3a3f><button class="button" data-v-56bf3a3f><span class="button-text" data-v-56bf3a3f>API</span><span class="right button-arrow" data-v-56bf3a3f></span></button><ul class="dialog" data-v-56bf3a3f><!--[--><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/api/@schlechtenburg/core" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>@schlechtenburg/core</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><!--]--></ul></div></div><!--]--><!----><!----></nav><!--[--><!--]--><ul class="sidebar-links" data-v-83e92a68><!--[--><li class="sidebar-link"><p class="sidebar-link-item">Getting Started</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/guide/why">Why Schlechtenburg?</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item active" href="/guide/installation">Installation</a><!----></li></ul></li><!--]--></ul><!--[--><!--]--></aside><div class="sidebar-mask"></div><main class="page" data-v-7eddb2c4><div class="container" data-v-7eddb2c4><!--[--><!--]--><div style="position:relative;" class="content" data-v-7eddb2c4><div><h1 id="installation" tabindex="-1">Installation <a class="header-anchor" href="#installation" aria-hidden="true">#</a></h1><p>First, install the editor core and any blocks you want to use:</p><div class="language-"><pre><code>npm i --save @schlechtenburg/core \
@schlechtenburg/layout \
@schlechtenburg/heading \
@schlechtenburg/paragraph
</code></pre></div></div></div><footer class="page-footer" data-v-7eddb2c4 data-v-07c132fc><div class="edit" data-v-07c132fc><div class="edit-link" data-v-07c132fc data-v-1ed99556><!----></div></div><div class="updated" data-v-07c132fc><!----></div></footer><div class="next-and-prev-link" data-v-7eddb2c4 data-v-38ede35f><div class="container" data-v-38ede35f><div class="prev" data-v-38ede35f><a class="link" href="/guide/why" data-v-38ede35f><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="icon icon-prev" data-v-38ede35f><path d="M19,11H7.4l5.3-5.3c0.4-0.4,0.4-1,0-1.4s-1-0.4-1.4,0l-7,7c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.2-0.1,0.5,0,0.8c0.1,0.1,0.1,0.2,0.2,0.3l7,7c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3c0.4-0.4,0.4-1,0-1.4L7.4,13H19c0.6,0,1-0.4,1-1S19.6,11,19,11z"></path></svg><span class="text" data-v-38ede35f>Why Schlechtenburg?</span></a></div><div class="next" data-v-38ede35f><!----></div></div></div><!--[--><!--]--></div></main></div><!----><!--]--></div>
<script>__VP_HASH_MAP__ = JSON.parse("{\"guide_installation.md\":\"7ba80841\",\"guide_introduction.md\":\"8be80f21\",\"guide_why.md\":\"3de43223\",\"index.md\":\"5cef284c\"}")</script>
<script type="module" async src="/assets/app.2b3cbed9.js"></script>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Introduction | Schlechtenburg</title>
<meta name="description" content="Experimental WYSIWYG block editor">
<link rel="stylesheet" href="/assets/style.a7ab32f9.css">
<link rel="modulepreload" href="/assets/app.2b3cbed9.js">
<link rel="modulepreload" href="/assets/guide_introduction.md.8be80f21.lean.js">
<meta name="twitter:title" content="Introduction | Schlechtenburg">
<meta property="og:title" content="Introduction | Schlechtenburg">
</head>
<body>
<div id="app"><!--[--><div class="theme"><header class="nav-bar" data-v-675d8756><div class="sidebar-button" data-v-675d8756><svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class></path></svg></div><a class="nav-bar-title" href="/" aria-label="Schlechtenburg, back to home" data-v-675d8756 data-v-cc01ef16><!----> Schlechtenburg</a><div class="flex-grow" data-v-675d8756></div><div class="nav" data-v-675d8756><nav class="nav-links" data-v-675d8756 data-v-eab3edfe><!--[--><div class="item" data-v-eab3edfe><div class="nav-link" data-v-eab3edfe data-v-b8818f8c><a class="item active" href="/guide/introduction" data-v-b8818f8c>Guide <!----></a></div></div><div class="item" data-v-eab3edfe><div class="nav-dropdown-link" data-v-eab3edfe data-v-56bf3a3f><button class="button" data-v-56bf3a3f><span class="button-text" data-v-56bf3a3f>API</span><span class="right button-arrow" data-v-56bf3a3f></span></button><ul class="dialog" data-v-56bf3a3f><!--[--><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/api/@schlechtenburg/core" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>@schlechtenburg/core</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><!--]--></ul></div></div><!--]--><!----><!----></nav></div><!--[--><!--]--></header><aside class="sidebar" data-v-83e92a68><nav class="nav-links nav" data-v-83e92a68 data-v-eab3edfe><!--[--><div class="item" data-v-eab3edfe><div class="nav-link" data-v-eab3edfe data-v-b8818f8c><a class="item active" href="/guide/introduction" data-v-b8818f8c>Guide <!----></a></div></div><div class="item" data-v-eab3edfe><div class="nav-dropdown-link" data-v-eab3edfe data-v-56bf3a3f><button class="button" data-v-56bf3a3f><span class="button-text" data-v-56bf3a3f>API</span><span class="right button-arrow" data-v-56bf3a3f></span></button><ul class="dialog" data-v-56bf3a3f><!--[--><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/api/@schlechtenburg/core" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>@schlechtenburg/core</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><!--]--></ul></div></div><!--]--><!----><!----></nav><!--[--><!--]--><ul class="sidebar-links" data-v-83e92a68><!--[--><li class="sidebar-link"><p class="sidebar-link-item">Getting Started</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/guide/why">Why Schlechtenburg?</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/guide/installation">Installation</a><!----></li></ul></li><!--]--></ul><!--[--><!--]--></aside><div class="sidebar-mask"></div><main class="page" data-v-7eddb2c4><div class="container" data-v-7eddb2c4><!--[--><!--]--><div style="position:relative;" class="content" data-v-7eddb2c4><div><h1 id="introduction" tabindex="-1">Introduction <a class="header-anchor" href="#introduction" aria-hidden="true">#</a></h1></div></div><footer class="page-footer" data-v-7eddb2c4 data-v-07c132fc><div class="edit" data-v-07c132fc><div class="edit-link" data-v-07c132fc data-v-1ed99556><!----></div></div><div class="updated" data-v-07c132fc><!----></div></footer><!----><!--[--><!--]--></div></main></div><!----><!--]--></div>
<script>__VP_HASH_MAP__ = JSON.parse("{\"guide_installation.md\":\"7ba80841\",\"guide_introduction.md\":\"8be80f21\",\"guide_why.md\":\"3de43223\",\"index.md\":\"5cef284c\"}")</script>
<script type="module" async src="/assets/app.2b3cbed9.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"guide_installation.md":"7ba80841","guide_introduction.md":"8be80f21","guide_why.md":"3de43223","index.md":"5cef284c"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,36 @@
:root {
--c-grey: #eeeeee;
--c-brand-dark: #378863;
}
.button {
display: inline-block;
cursor: pointer;
text-decoration: none;
padding: 0.5rem 1rem;
color: var(--c-black);
background: var(--c-grey);
border: 0;
font-size: 1rem;
line-height: 1.6rem;
border-radius: 0.3rem;
}
.button:hover {
text-decoration: none;
}
.button_cta {
color: var(--c-white);
background: var(--c-brand-dark);
}
.cta-row {
display: flex;
justify-content: center;
margin: 4rem;
}
.cta-row .button {
margin: 0.5rem;
}

View file

@ -0,0 +1,5 @@
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme

View file

@ -1,22 +0,0 @@
.app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
display: flex;
&--sidemenu {
width: 300px;
padding: 3rem 1rem;
}
&--main {
padding: 3rem 4rem;
margin: 0 auto;
flex-basis: 700px;
flex-shrink: 1;
flex-grow: 0;
display: flex;
flex-direction: column;
}
}

View file

@ -1,18 +0,0 @@
import {
defineComponent,
} from 'vue';
import { RouterView } from 'vue-router';
import SideMenu from './sidemenu/Sidemenu';
import './App.scss';
export default defineComponent({
name: 'App',
setup() {
return () => <div class="app">
<SideMenu class="app--sidemenu"></SideMenu>
<RouterView class="app--main"></RouterView>
</div>;
},
});

View file

@ -0,0 +1,17 @@
.example-editor {
display: flex;
flex-direction: column;
&--title {
display: flex;
justify-content: space-between;
align-items: center;
}
&--mode { }
&--sb {
display: flex;
flex-direction: column;
}
}

View file

@ -13,31 +13,22 @@ import SbHeading from '@schlechtenburg/heading';
import SbParagraph from '@schlechtenburg/paragraph'; import SbParagraph from '@schlechtenburg/paragraph';
import SbImage from '@schlechtenburg/image'; import SbImage from '@schlechtenburg/image';
import './App.scss'; import exampleData from './example-data';
import './ExampleEditor.scss';
export default defineComponent({ export default defineComponent({
name: 'App', name: 'ExampleEditor',
setup() { setup() {
const activeTab = ref('edit'); const activeTab = ref('edit');
const block: IBlockData<any> = reactive({ const block: IBlockData<any> = reactive({ ...exampleData });
name: 'none',
id: '0',
data: null,
});
onBeforeMount(async () => {
const res = await fetch('./initial-data.json');
const data = await res.json();
block.name = data.name;
block.id = data.id;
block.data = data.data;
});
const displayedElement = computed(() => { const displayedElement = computed(() => {
switch (activeTab.value) { switch (activeTab.value) {
case SbMode.Edit: case SbMode.Edit:
return <SbMain return <SbMain
class="example-editor--sb"
block={block} block={block}
onUpdate={(newBlock: IBlockData<any>) => { onUpdate={(newBlock: IBlockData<any>) => {
block.data = newBlock.data; block.data = newBlock.data;
@ -53,6 +44,7 @@ export default defineComponent({
/>; />;
case SbMode.Display: case SbMode.Display:
return <SbMain return <SbMain
class="example-editor--sb"
block={block} block={block}
availableBlocks={[ availableBlocks={[
SbLayout, SbLayout,
@ -69,18 +61,21 @@ export default defineComponent({
}); });
return () => { return () => {
return <div class="app"> return <div class="example-editor">
<select <h2 class="example-editor--title">
class="app--mode" <span>Try it yourself</span>
value={activeTab.value} <select
onChange={($event: Event) => { class="example-editor--mode"
activeTab.value = ($event.target as HTMLSelectElement).value; value={activeTab.value}
}} onChange={($event: Event) => {
> activeTab.value = ($event.target as HTMLSelectElement).value;
<option>edit</option> }}
<option>display</option> >
<option>data</option> <option value="edit">Editor mode</option>
</select> <option value="display">Display mode</option>
<option value="data">JSON Data structure</option>
</select>
</h2>
{displayedElement.value} {displayedElement.value}
</div>; </div>;
}; };

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,12 @@
# Installation
First, install the editor core and any blocks you want to use:
```
npm i --save @schlechtenburg/core \
@schlechtenburg/layout \
@schlechtenburg/heading \
@schlechtenburg/paragraph
```

View file

@ -0,0 +1,3 @@
# Introduction

View file

@ -0,0 +1,80 @@
# Why Schlechtenburg?
Installing a WYSIWYG editor in your application or on your website is often the easy part. The hard
part comes afterwards: extending and customizing the editor to fit your specific needs. There are
currently a couple of very good editors, but after reading this text I think you'll agree there's
still room for improvement. We'll be looking at the following alternative WYSIWYG editors:
* [Quill](https://quilljs.com/)
* [CKEditor](https://ckeditor.com/ckeditor-5/)
* [Gutenberg](https://wordpress.org/gutenberg/)
## Block based
Though all three are meant for text editing, Quill and CKEditor are a bit more explicit about this
in their architecture:
* They input and output a string
* They have one global toolbar
Gutenberg is a bit more involved, literally using building "blocks" to create it's editor. Instead
of seeing the content as a long string it takes a more component-esque approach. For example, the
following things are all their own blocks in the gutenberg editor, which a specific react component
that handles the editing mode, and one that handles the display mode.
* Paragraph
* Heading
* List
* Image
* Column-based layout
* External media embeds like YouTube videos
When editing, you are editing, adding or removing one specific block at a time. Blocks can contain
other blocks in a tree structure, and they all have their own (but uniform) editing UI.
Schlechtenburg takes a block based approach, just like its namesake Gutenberg. This has a couple of advantages:
* You can take strong control over the final rendered HTML of a block that you create
* Blocks are easily published as reusable JS modules
* You very rarely need blocks inside text, but the reverse has abundant usecases
* The mental model is closer to how actual HTML works
* Very complex pages can be handled by the editor
* If you know React or Vue, you understand a lot about how to write blocks for the editor
## Design system with standardized components
Making sure the final rendered data looks correct is always easier than making sure the editing
experience is great. Schlechtenburg aims to offer a vast library of reusable components, patterns,
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
accessible.
## JSON only
Input and Output is one standardized, typed, JSON-stringifyable object. For example, a paragraph
looks like this:
```
{
id: '1590592116800',
name: 'sb-paragraph',
data: {
value: 'This is the second paragraph<br>',
align: 'left'
}
},
```
## So why not Gutenberg?
Gutenberg is tied heavily into the Wordpress ecosystem, making its inclusion in other sites harder
than necessary.

View file

@ -2,12 +2,18 @@
import ExampleEditor from './ExampleEditor' import ExampleEditor from './ExampleEditor'
</script> </script>
# Schlechtenburg # Yet another WYSIWYG editor
Schlechtenburg is an experimental WYSIWYG editor framework made with Vue 3 and TypeScript. It takes cues from both Wordpress' Gutenberg editor and CKEditor, though it tries to become a best of both worlds; a very lightweight, easily extensible core, written with modern components and the accompanying state management. Schlechtenburg is an experimental WYSIWYG editor framework made with Vue 3 and TypeScript. It takes cues from both Wordpress' Gutenberg editor and CKEditor, though it tries to become a best of both worlds; a very lightweight, easily extensible core, written with modern components and the accompanying state management.
It inputs and outputs a tree of JSON-serializable data. It inputs and outputs a tree of JSON-serializable data.
Just a plaything for now. This is still in the Proof-of-concept phase.
<div class="cta-row">
<a href="/guide/why" class="button button_cta">Why Schlechtenburg?</a>
<a href="/guide/introduction" class="button">Get Started</a>
<a href="/api" class="button">See the API docs</a>
</div>
<ExampleEditor></ExampleEditor> <ExampleEditor></ExampleEditor>

View file

@ -1,10 +0,0 @@
.sidemenu {
&--link {
color: #0080ff;
padding:
}
&--package-child {
}
}

View file

@ -1,25 +0,0 @@
import { defineComponent } from 'vue';
import {
core,
layout,
heading,
paragraph,
image,
} from '../docs';
import SidemenuPackage from './SidemenuPackage';
import './Sidemenu.scss';
export default defineComponent({
name: 'Sidemenu',
setup() {
return () => <div class="sidemenu">
<SidemenuPackage package={core}></SidemenuPackage>
<SidemenuPackage package={layout}></SidemenuPackage>
<SidemenuPackage package={heading}></SidemenuPackage>
<SidemenuPackage package={paragraph}></SidemenuPackage>
<SidemenuPackage package={image}></SidemenuPackage>
</div>;
},
});

View file

@ -1,44 +0,0 @@
import {
defineComponent,
PropType,
} from 'vue';
import { RouterLink } from 'vue-router';
import { getShortPackageName } from '../package';
import { IDocs } from '../docs';
export default defineComponent({
name: 'Sidemenu',
props: {
package: {
type: (null as unknown) as PropType<IDocs>,
required: true,
},
},
setup(props) {
const { lib, components } = props.package;
const shortName = getShortPackageName(lib.name);
return () => <div>
<RouterLink
class="sidemenu--link sidemenu--link_package"
to={{
name: 'package',
params: { package: shortName },
}}
>{lib.name}</RouterLink>
<ul class="sidemenu--package-children">
{...(lib.children || []).map(child => <li class="sidemenu--package-child">
<RouterLink
class="sidemenu--link"
to={{
name: 'package',
params: { package: shortName },
hash: '#' + child.name,
}}
>{child.name}</RouterLink>
</li>)}
</ul>
</div>;
},
});

View file

@ -1,8 +1,3 @@
{ {
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": {
"paths": {
// "~/*": ["./lib/*"]
}
}
} }

View file

@ -169,31 +169,33 @@ export default defineComponent({
>{localData.orientation}</SbButton> >{localData.orientation}</SbButton>
</SbToolbar> </SbToolbar>
{...localData.children.map((child, index) => ( <>
<SbBlock {...localData.children.map((child, index) => (
{...{ key: child.id }} <SbBlock
data-order={index} {...{ key: child.id }}
block={child} data-order={index}
onUpdate={(updated: IBlockData<any>) => onChildUpdate(child, updated)} block={child}
onRemoveSelf={() => removeBlock(index)} onUpdate={(updated: IBlockData<any>) => onChildUpdate(child, updated)}
onPrependBlock={(block: IBlockData<any>) => insertBlock(index - 1, block)} onRemoveSelf={() => removeBlock(index)}
onAppendBlock={(block: IBlockData<any>) => insertBlock(index, block)} onPrependBlock={(block: IBlockData<any>) => insertBlock(index - 1, block)}
onActivatePrevious={() => activateBlock(index - 1,)} onAppendBlock={(block: IBlockData<any>) => insertBlock(index, block)}
onActivateNext={() => activateBlock(index + 1,)} onActivatePrevious={() => activateBlock(index - 1,)}
> onActivateNext={() => activateBlock(index + 1,)}
{{ >
'context-toolbar': () => {{
<SbBlockOrdering 'context-toolbar': () =>
onMoveBackward={() => moveBackward(index)} <SbBlockOrdering
onMoveForward={() => moveForward(index)} onMoveBackward={() => moveBackward(index)}
onRemove={() => removeBlock(index)} onMoveForward={() => moveForward(index)}
orientation={localData.orientation} onRemove={() => removeBlock(index)}
/>, orientation={localData.orientation}
}} />,
</SbBlock> }}
))} </SbBlock>
))}
</>
<SbBlockPlaceholder onInsertBlock={appendBlock} /> <SbBlockPlaceholder onInsertBlock={appendBlock}></SbBlockPlaceholder>
</div> </div>
); );
}, },

View file

@ -0,0 +1,11 @@
# `@schlechtenburg/core`
> TODO: description
## Usage
```
const core = require('@schlechtenburg/core');
// TODO: DEMONSTRATE API
```

View file

@ -0,0 +1,7 @@
'use strict';
const core = require('..');
describe('@schlechtenburg/core', () => {
it('needs tests');
});

View file

@ -0,0 +1,56 @@
import {
defineComponent,
PropType,
} from 'vue';
import {
OnUpdateBlockCb,
IBlockDefinition,
IBlockData,
SbMode,
SbMain,
} from '@schlechtenburg/core';
export interface ISchlechtenburgProps {
availableBlocks: IBlockDefinition<any>[];
block: IBlockData<any>;
onUpdate: OnUpdateBlockCb;
mode: SbMode;
}
export const Schlechtenburg = defineComponent({
name: 'schlechtenburg',
props: {
availableBlocks: {
type: Array as PropType<IBlockDefinition<any>[]>,
default: () => [],
},
block: {
type: Object as PropType<IBlockData<any>>,
required: true,
},
/**
* Called when the block should be updated.
*/
onUpdate: {
type: (null as unknown) as PropType<OnUpdateBlockCb>,
default: () => {},
},
mode: {
type: String as PropType<SbMode>,
validator(value: any) {
return Object.values(SbMode).includes(value);
},
default: SbMode.Edit,
},
},
setup(props: ISchlechtenburgProps) {
return () => <SbMain
availableBlocks={props.availableBlocks}
block={props.block}
onUpdate={props.onUpdate}
mode={props.mode}
/>;
},
});

View file

@ -0,0 +1 @@
export * from './main';

View file

@ -0,0 +1,25 @@
import { createApp } from 'vue'
import {
ISchlechtenburgProps,
Schlechtenburg,
} from './Schlechtenburg';
/**
*
*/
export const startSchlechtenburg = async (
/**
* The element on which the editor schould be mounted
*/
el:HTMLElement|string,
/**
* The schlechtenburg props
*/
props:ISchlechtenburgProps,
) => {
const app = createApp(Schlechtenburg, props as unknown as Record<string, unknown>);
app.mount(el);
return app;
}

View file

@ -0,0 +1,64 @@
declare namespace NodeJS {
interface Process{
env: ProcessEnv
}
interface ProcessEnv {
/**
* By default, there are two modes in Vite:
*
* * `development` is used by vite and vite serve
* * `production` is used by vite build
*
* You can overwrite the default mode used for a command by passing the --mode option flag.
*
*/
readonly NODE_ENV: 'development' | 'production'
}
}
declare var process: NodeJS.Process
declare module '*.gif' {
const src: string
export default src
}
declare module '*.jpg' {
const src: string
export default src
}
declare module '*.jpeg' {
const src: string
export default src
}
declare module '*.png' {
const src: string
export default src
}
declare module '*.webp' {
const src: string
export default src
}
declare module '*.svg' {
const src: string;
export default src
}
declare module '*.module.css' {
const classes: { readonly [key: string]: string }
export default classes
}
declare module '*.module.scss' {
const classes: { readonly [key: string]: string }
export default classes
}
declare module '*.module.sass' {
const classes: { readonly [key: string]: string }
export default classes
}

1317
packages/standalone/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
{
"name": "@schlechtenburg/standalone",
"version": "0.0.0",
"description": "> TODO: description",
"author": "Benjamin Bädorf <hello@benjaminbaedorf.eu>",
"homepage": "",
"license": "GPL-3.0-or-later",
"main": "lib/index.ts",
"scripts": {
"typecheck": "vuedx-typecheck --no-pretty ./lib",
"docgen": "docgen"
},
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib",
"docs"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git@git.b12f.io:b12f/schlechtenburg.git"
},
"dependencies": {
"@schlechtenburg/core": "^0.0.0",
"vue": "^3.0.7"
},
"devDependencies": {
"@vuedx/typecheck": "^0.6.3",
"@vuedx/typescript-plugin-vue": "^0.6.3",
"@schlechtenburg/docgen": "^0.0.0",
"@types/lodash-es": "^4.17.4",
"@types/uuid": "^8.3.0"
}
}

View file

@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.json",
"typedocOptions": {
"entryPoints": ["lib/index.ts"],
"json": "docs"
}
}