Rework typing
This commit is contained in:
parent
869c4d1a28
commit
4fc087a752
64 changed files with 490 additions and 362 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ node_modules
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
tags
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
var a=Object.assign;import{z as s,m as t,b as e,A as l,E as i,f as r}from"./index.039d73cc.js";/* empty css */import"./vendor.9babb3f5.js";var d=s({name:"sb-image-display",model:t,props:a(a({},e),{data:{type:null,default:l}}),setup:a=>()=>i("figure",{class:"sb-image"},[i("img",{class:"sb-image__content",src:a.data.src,alt:a.data.alt},null),i(r,{block:a.data.description},null)])});export default d;
|
|
|
@ -1 +0,0 @@
|
||||||
var e=Object.assign;import{H as t,m as n,b as a,I as s,J as l}from"./index.039d73cc.js";import"./vendor.9babb3f5.js";var o=t({name:"sb-missing-block",model:n,props:e(e({name:String},a),{data:{type:null,default:null},eventUpdate:{type:Function,default:()=>{}},eventAppendBlock:{type:Function,default:()=>{}},eventRemoveBlock:{type:Function,default:()=>{}}}),setup:e=>()=>s("div",{class:"sb-missing-block"},[l("Missing block: "),e.name])});export default o;
|
|
|
@ -1 +0,0 @@
|
||||||
var a=Object.assign;import{d as s,m as t,b as e,g as l,c as o,a as d,f as r}from"./index.039d73cc.js";/* empty css */import"./vendor.9babb3f5.js";var i=s({name:"sb-layout-display",model:t,props:a(a({},e),{data:{type:null,default:l}}),setup(a){const s=o((()=>({"sb-layout":!0,[`sb-layout_${a.data.orientation}`]:!0})));return()=>d("div",{class:s.value},[...a.data.children.map((a=>d(r,{key:a.id,block:a},null)))])}});export default i;
|
|
1
docs/assets/display.65dc313e.js
Normal file
1
docs/assets/display.65dc313e.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import{d as a,m as s,g as t,c as e,a as l,e as o}from"./index.be3c5d52.js";/* empty css */import"./vendor.9babb3f5.js";var d=a({name:"sb-layout-display",model:s,props:{data:{type:null,default:t}},setup(a){const s=e((()=>({"sb-layout":!0,[`sb-layout_${a.data.orientation}`]:!0})));return()=>l("div",{class:s.value},[...a.data.children.map((a=>l(o,{key:a.id,block:a},null)))])}});export default d;
|
1
docs/assets/display.8a67d4c4.js
Normal file
1
docs/assets/display.8a67d4c4.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import{y as a,m as s,z as e,D as t,e as l}from"./index.be3c5d52.js";/* empty css */import"./vendor.9babb3f5.js";var d=a({name:"sb-image-display",model:s,props:{data:{type:null,default:e}},setup:a=>()=>t("figure",{class:"sb-image"},[t("img",{class:"sb-image__content",src:a.data.src,alt:a.data.alt},null),t(l,{block:a.data.description},null)])});export default d;
|
1
docs/assets/display.9a74f321.js
Normal file
1
docs/assets/display.9a74f321.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
var e=Object.assign;import{G as t,m as n,H as a,I as s,J as l}from"./index.be3c5d52.js";import"./vendor.9babb3f5.js";var o=t({name:"sb-missing-block",model:n,props:e(e({},a),{name:String,data:{type:null,default:null},eventUpdate:{type:Function,default:()=>{}},eventAppendBlock:{type:Function,default:()=>{}},eventRemoveBlock:{type:Function,default:()=>{}}}),setup:e=>()=>s("div",{class:"sb-missing-block"},[l("Missing block: "),e.name])});export default o;
|
|
@ -1 +0,0 @@
|
||||||
var a=Object.assign;import{k as s,m as r,b as t,l as e,s as p,t as l,y as n}from"./index.039d73cc.js";/* empty css */import"./vendor.9babb3f5.js";var b=s({name:"sb-paragraph-display",model:r,props:a(a({},t),{data:{type:Object,default:e}}),setup(a){const s=p((()=>({"sb-paragraph":!0,[`sb-paragraph_align-${a.data.align}`]:!0})));return()=>l("p",n({class:s.value},{innerHTML:a.data.value}),null)}});export default b;
|
|
1
docs/assets/display.e17abc85.js
Normal file
1
docs/assets/display.e17abc85.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import{j as a,m as s,k as r,q as e,s as p,x as t}from"./index.be3c5d52.js";/* empty css */import"./vendor.9babb3f5.js";var l=a({name:"sb-paragraph-display",model:s,props:{data:{type:Object,default:r}},setup(a){const s=e((()=>({"sb-paragraph":!0,[`sb-paragraph_align-${a.data.align}`]:!0})));return()=>p("p",t({class:s.value},{innerHTML:a.data.value}),null)}});export default l;
|
1
docs/assets/edit.1f668f17.js
Normal file
1
docs/assets/edit.1f668f17.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
var e=Object.assign;import{d as n,m as i,g as t,u as l,r as a,w as d,c as r,a as c,S as o,b as h,e as s,f as p,h as u,i as v}from"./index.be3c5d52.js";/* empty css */import"./vendor.9babb3f5.js";var f=n({name:"sb-layout-edit",model:i,props:{onUpdate:{type:Function,default:()=>{}},data:{type:null,default:t}},setup(n){const{activate:i}=l(),t=a({orientation:n.data.orientation,children:[...n.data.children]});d((()=>n.data),(()=>{t.orientation=n.data.orientation,t.children=[...n.data.children]}));const f=r((()=>({"sb-layout":!0,[`sb-layout_${t.orientation}`]:!0}))),m=()=>{n.onUpdate({orientation:"vertical"===t.orientation?"horizontal":"vertical"})},b=e=>{t.children=[...t.children,e],n.onUpdate({children:[...t.children]}),i(e.id)},U=(e,l)=>{t.children=[...t.children.slice(0,e+1),l,...t.children.slice(e+1)],n.onUpdate({children:[...t.children]}),i(l.id)},y=e=>{t.children=[...t.children.slice(0,e),...t.children.slice(e+1)],n.onUpdate({children:[...t.children]});const l=Math.max(e-1,0);i(t.children[l].id)},k=e=>{const n=Math.max(Math.min(t.children.length-1,e),0);i(t.children[n].id)};return()=>c("div",{class:f.value},[c(o,null,{default:()=>[c(h,{type:"button",onClick:m},{default:()=>[t.orientation]})]}),...t.children.map(((i,l)=>c(s,p({key:i.id},{"data-order":l,block:i,onUpdate:l=>((i,l)=>{const a=t.children.indexOf(i);-1!==a&&n.onUpdate({children:[...t.children.slice(0,a),e(e({},i),l),...t.children.slice(a+1)]})})(i,l),onRemoveSelf:()=>y(l),onPrependBlock:e=>U(l-1,e),onAppendBlock:e=>U(l,e),onActivatePrevious:()=>k(l-1),onActivateNext:()=>k(l+1)}),{"context-toolbar":()=>c(u,{onMoveBackward:()=>(e=>{if(0===e)return;const i=t.children[e],l=t.children[e-1];t.children=[...t.children.slice(0,e-1),i,l,...t.children.slice(e+1)],n.onUpdate({children:[...t.children]})})(l),onMoveForward:()=>(e=>{if(e===t.children.length-1)return;const i=t.children[e],l=t.children[e+1];t.children=[...t.children.slice(0,e),l,i,...t.children.slice(e+2)],n.onUpdate({children:[...t.children]})})(l),onRemove:()=>y(l),orientation:t.orientation},null)}))),c(v,{onInsertBlock:b},null)])}});export default f;
|
1
docs/assets/edit.25f6c7d7.js
Normal file
1
docs/assets/edit.25f6c7d7.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import{j as a,m as e,k as n,l as t,n as l,u as o,o as u,p as i,q as s,s as r,S as d,t as p,v}from"./index.be3c5d52.js";/* empty css */import"./vendor.9babb3f5.js";var c=a({name:"sb-paragraph-edit",model:e,props:{blockId:{type:String,required:!0},data:{type:null,default:n},onUpdate:{type:Function,default:()=>{}},onAppendBlock:{type:Function,default:()=>{}},onRemoveSelf:{type:Function,default:()=>{}},onActivateNext:{type:Function,default:()=>{}},onActivatePrevious:{type:Function,default:()=>{}}},setup(a){const e=t({value:a.data.value,align:a.data.align,focused:!1}),c=l(null),{isActive:f,activate:g}=o(a.blockId),b=()=>{c.value&&f.value&&c.value.focus()};u((()=>{b(),c.value&&(c.value.innerHTML=e.value)})),i(f,b),i((()=>a.data),(()=>{e.value=a.data.value,e.align=a.data.align,c.value&&(c.value.innerHTML=e.value)}));const y=a=>{e.value=a.target.innerHTML},h=s((()=>({"sb-paragraph":!0,"sb-paragraph_focused":e.focused,[`sb-paragraph_align-${e.align}`]:!0}))),m=n=>{a.onUpdate({value:e.value,align:n.target.value})},k=()=>{e.focused=!0,g()},A=()=>{e.focused=!1,a.onUpdate({value:e.value,align:e.align})},w=e=>{if("Enter"===e.key&&!e.shiftKey){const t=""+ +new Date;a.onAppendBlock({id:t,name:"sb-paragraph",data:n()}),g(t),e.preventDefault()}},F=n=>{var t;"Backspace"===n.key&&""===e.value&&a.onRemoveSelf();const l=window.getSelection(),o=null==l?void 0:l.focusNode,u=Array.from((null==(t=null==c?void 0:c.value)?void 0:t.childNodes)||[]),i=o?u.indexOf(o):-1;if(o===c.value||0===i||i===u.length-1)switch(n.key){case"ArrowDown":a.onActivateNext();break;case"ArrowUp":a.onActivatePrevious()}};return()=>r("div",{class:h.value},[r(d,null,{default:()=>[r(p,{value:e.align,onChange:m},{default:()=>[r("option",null,[v("left")]),r("option",null,[v("center")]),r("option",null,[v("right")])]})]}),r("p",{class:"sb-paragraph__input",ref:c,contenteditable:!0,onInput:y,onFocus:k,onBlur:A,onKeydown:w,onKeyup:F},null)])}});export default c;
|
|
@ -1 +0,0 @@
|
||||||
var a=Object.assign;import{k as e,m as n,b as t,l,n as o,o as u,u as s,p as i,q as r,s as p,t as d,S as c,v,x as f}from"./index.039d73cc.js";/* empty css */import"./vendor.9babb3f5.js";var g=e({name:"sb-paragraph-edit",model:n,props:a(a({},t),{data:{type:null,default:l},onUpdate:{type:Function,default:()=>{}},onAppendBlock:{type:Function,default:()=>{}},onRemoveSelf:{type:Function,default:()=>{}},onActivateNext:{type:Function,default:()=>{}},onActivatePrevious:{type:Function,default:()=>{}}}),setup(a){const e=o({value:a.data.value,align:a.data.align,focused:!1}),n=u(null),{isActive:t,activate:g}=s(a.blockId),b=()=>{n.value&&t.value&&n.value.focus()};i((()=>{b(),n.value&&(n.value.innerHTML=e.value)})),r(t,b),r((()=>a.data),(()=>{e.value=a.data.value,e.align=a.data.align,n.value&&(n.value.innerHTML=e.value)}));const y=a=>{e.value=a.target.innerHTML},h=p((()=>({"sb-paragraph":!0,"sb-paragraph_focused":e.focused,[`sb-paragraph_align-${e.align}`]:!0}))),m=n=>{a.onUpdate({value:e.value,align:n.target.value})},A=()=>{e.focused=!0,g()},k=()=>{e.focused=!1,a.onUpdate({value:e.value,align:e.align})},w=e=>{if("Enter"===e.key&&!e.shiftKey){const n=""+ +new Date;a.onAppendBlock({id:n,name:"sb-paragraph",data:l()}),g(n),e.preventDefault()}},x=t=>{"Backspace"===t.key&&""===e.value&&a.onRemoveSelf();const l=window.getSelection().focusNode,o=Array.from(n.value.childNodes),u=o.indexOf(l);if(l===n.value||0===u||u===o.length-1)switch(t.key){case"ArrowDown":a.onActivateNext();break;case"ArrowUp":a.onActivatePrevious()}};return()=>d("div",{class:h.value},[d(c,null,{default:()=>[d(v,{value:e.align,onChange:m},{default:()=>[d("option",null,[f("left")]),d("option",null,[f("center")]),d("option",null,[f("right")])]})]}),d("p",{class:"sb-paragraph__input",ref:n,contenteditable:!0,onInput:y,onFocus:A,onBlur:k,onKeydown:w,onKeyup:x},null)])}});export default g;
|
|
|
@ -1 +0,0 @@
|
||||||
var a=Object.assign;import{z as e,m as t,b as s,A as l,B as n,C as d,D as i,E as r,S as c,e as o,F as u,G as p,f}from"./index.039d73cc.js";/* empty css */import"./vendor.9babb3f5.js";var m=e({name:"sb-image-edit",model:t,props:a(a({},s),{onUpdate:{type:Function,default:()=>{}},data:{type:null,default:l}}),setup(e){const t=n({src:e.data.src,alt:e.data.alt,description:e.data.description}),s=d(null);i((()=>e.data),(()=>{t.src=e.data.src,t.alt=e.data.alt,t.description=e.data.description}));const l=()=>{s.value&&s.value.click()},m=()=>{if(s.value&&s.value.files&&s.value.files.length){const a=new FileReader;a.addEventListener("load",(()=>{e.onUpdate({src:a.result,alt:e.data.alt,description:e.data.description})})),a.readAsDataURL(s.value.files[0])}};return()=>r("figure",{class:"sb-image"},[r(c,null,{default:()=>[t.src?r(o,{onClick:l},{default:()=>[u("Change Image")]}):null,r("input",{type:"file",ref:s,style:"display: none;",onInput:m},null)]}),t.src?r(p,null,[r("img",{src:t.src,alt:t.alt,class:"sb-image__content"},null),r(f,{block:t.description,onUpdate:t=>{return s=t,void e.onUpdate(a(a({},e.data),{description:s}));var s}},null)]):r(o,{onClick:l},{default:()=>[u("Select Image")]})])}});export default m;
|
|
|
@ -1 +0,0 @@
|
||||||
var e=Object.assign;import{d as n,m as i,b as l,g as t,u as a,r as d,w as r,c,a as o,S as s,e as h,f as p,h as u,i as v,j as b}from"./index.039d73cc.js";/* empty css */import"./vendor.9babb3f5.js";var f=n({name:"sb-layout-edit",model:i,props:e(e({},l),{onUpdate:{type:Function,default:()=>{}},data:{type:null,default:t}}),setup(n){const{activate:i}=a(n.id),l=d({orientation:n.data.orientation,children:[...n.data.children]});r((()=>n.data),(()=>{l.orientation=n.data.orientation,l.children=[...n.data.children]}));const t=c((()=>({"sb-layout":!0,[`sb-layout_${l.orientation}`]:!0}))),f=()=>{console.log("toggle"),n.onUpdate({orientation:"vertical"===l.orientation?"horizontal":"vertical"})},m=e=>{l.children=[...l.children,e],n.onUpdate({children:[...l.children]}),i(e.id)},U=(e,t)=>{l.children=[...l.children.slice(0,e+1),t,...l.children.slice(e+1)],n.onUpdate({children:[...l.children]}),i(t.id)},y=e=>{l.children=[...l.children.slice(0,e),...l.children.slice(e+1)],n.onUpdate({children:[...l.children]});const t=Math.max(e-1,0);i(l.children[t].id)},g=e=>{const n=Math.max(Math.min(l.children.length-1,e),0);i(l.children[n].id)};return()=>o("div",{class:t.value},[o(s,null,{default:()=>[o(h,{type:"button",onClick:f},{default:()=>[l.orientation]})]}),...l.children.map(((i,t)=>o(p,u({key:i.id},{"data-order":t,block:i,onUpdate:t=>((i,t)=>{const a=l.children.indexOf(i);-1!==a&&n.onUpdate({children:[...l.children.slice(0,a),e(e({},i),t),...l.children.slice(a+1)]})})(i,t),onRemoveSelf:()=>y(t),onPrependBlock:e=>U(t-1,e),onAppendBlock:e=>U(t,e),onActivatePrevious:e=>g(t-1),onActivateNext:e=>g(t+1)}),{"context-toolbar":()=>o(v,{onMoveBackward:()=>(e=>{if(0===e)return;const i=l.children[e],t=l.children[e-1];l.children=[...l.children.slice(0,e-1),i,t,...l.children.slice(e+1)],n.onUpdate({children:[...l.children]})})(t),onMoveForward:()=>(e=>{if(e===l.children.length-1)return;const i=l.children[e],t=l.children[e+1];l.children=[...l.children.slice(0,e),t,i,...l.children.slice(e+2)],n.onUpdate({children:[...l.children]})})(t),onRemove:()=>y(t),sortable:n.sortable},null)}))),o(b,{onInsertBlock:m},null)])}});export default f;
|
|
1
docs/assets/edit.fbd6e1aa.js
Normal file
1
docs/assets/edit.fbd6e1aa.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
var a=Object.assign;import{y as e,m as t,z as l,A as s,B as n,C as i,D as d,S as r,b as o,E as c,F as u,e as p}from"./index.be3c5d52.js";/* empty css */import"./vendor.9babb3f5.js";var v=e({name:"sb-image-edit",model:t,props:{onUpdate:{type:Function,default:()=>{}},data:{type:null,default:l}},setup(e){const t=s({src:e.data.src,alt:e.data.alt,description:e.data.description}),l=n(null);i((()=>e.data),(()=>{t.src=e.data.src,t.alt=e.data.alt,t.description=e.data.description}));const v=()=>{l.value&&l.value.click()},f=()=>{if(l.value&&l.value.files&&l.value.files.length){const a=new FileReader;a.addEventListener("load",(()=>{var t;const l=null==(t=null==a?void 0:a.result)?void 0:t.toString();if(!l)throw new Error("Couldn't load image src");e.onUpdate({src:l,alt:e.data.alt,description:e.data.description})})),a.readAsDataURL(l.value.files[0])}};return()=>d("figure",{class:"sb-image"},[d(r,null,{default:()=>[t.src?d(o,{"onClick:value":v},{default:()=>[c("Select Image")]}):null,d("input",{type:"file",ref:l,style:"display: none;",onInput:f},null)]}),t.src?d(u,null,[d("img",{src:t.src,alt:t.alt,class:"sb-image__content"},null),d(p,{block:t.description,onUpdate:t=>{return l=t,void e.onUpdate(a(a({},e.data),{description:l}));var l}},null)]):d(o,{"onClick:value":v},{default:()=>[c("Select Image")]})])}});export default v;
|
File diff suppressed because one or more lines are too long
1
docs/assets/index.be3c5d52.js
Normal file
1
docs/assets/index.be3c5d52.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -2,12 +2,12 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/schlechtenburg/favicon.ico" />
|
<link rel="icon" href="./favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Vite App</title>
|
<title>Vite App</title>
|
||||||
<script type="module" crossorigin src="/schlechtenburg/assets/index.039d73cc.js"></script>
|
<script type="module" crossorigin src="./assets/index.be3c5d52.js"></script>
|
||||||
<link rel="modulepreload" href="/schlechtenburg/assets/vendor.9babb3f5.js">
|
<link rel="modulepreload" href="./assets/vendor.9babb3f5.js">
|
||||||
<link rel="stylesheet" href="/schlechtenburg/assets/index.62d1366f.css">
|
<link rel="stylesheet" href="./assets/index.62d1366f.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
127
package-lock.json
generated
127
package-lock.json
generated
|
@ -3394,6 +3394,115 @@
|
||||||
"typedarray": "^0.0.6"
|
"typedarray": "^0.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"concurrently": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-Ik9Igqnef2ONLjN2o/OVx1Ow5tymVvvEwQeYCQdD/oV+CN9oWhxLk7ibcBdOtv0UzBqHCEKRwbKceYoTK8t3fQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"date-fns": "^2.16.1",
|
||||||
|
"lodash": "^4.17.20",
|
||||||
|
"read-pkg": "^5.2.0",
|
||||||
|
"rxjs": "^6.6.3",
|
||||||
|
"spawn-command": "^0.0.2-1",
|
||||||
|
"supports-color": "^8.1.0",
|
||||||
|
"tree-kill": "^1.2.2",
|
||||||
|
"yargs": "^16.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"parse-json": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/code-frame": "^7.0.0",
|
||||||
|
"error-ex": "^1.3.1",
|
||||||
|
"json-parse-even-better-errors": "^2.3.0",
|
||||||
|
"lines-and-columns": "^1.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"read-pkg": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/normalize-package-data": "^2.4.0",
|
||||||
|
"normalize-package-data": "^2.5.0",
|
||||||
|
"parse-json": "^5.0.0",
|
||||||
|
"type-fest": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "8.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||||
|
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type-fest": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"config-chain": {
|
"config-chain": {
|
||||||
"version": "1.1.12",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
|
||||||
|
@ -3806,6 +3915,12 @@
|
||||||
"assert-plus": "^1.0.0"
|
"assert-plus": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"date-fns": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"dateformat": {
|
"dateformat": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
||||||
|
@ -8232,6 +8347,12 @@
|
||||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"spawn-command": {
|
||||||
|
"version": "0.0.2-1",
|
||||||
|
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
|
||||||
|
"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"spdx-correct": {
|
"spdx-correct": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
|
||||||
|
@ -8627,6 +8748,12 @@
|
||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tree-kill": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"trim-newlines": {
|
"trim-newlines": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz",
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "concurrently 'vuedx-typecheck --no-pretty --watch ./src' 'vite'",
|
||||||
"build": "vuedx-typecheck . && vite build"
|
"build": "vuedx-typecheck --no-pretty ./src && vite build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash-es": "^4.17.20",
|
"lodash-es": "^4.17.20",
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
"@vue/compiler-sfc": "^3.0.5",
|
"@vue/compiler-sfc": "^3.0.5",
|
||||||
"@vuedx/typecheck": "^0.6.3",
|
"@vuedx/typecheck": "^0.6.3",
|
||||||
"@vuedx/typescript-plugin-vue": "^0.6.3",
|
"@vuedx/typescript-plugin-vue": "^0.6.3",
|
||||||
|
"concurrently": "^6.0.0",
|
||||||
"lerna": "^3.22.1",
|
"lerna": "^3.22.1",
|
||||||
"sass": "^1.30.0",
|
"sass": "^1.30.0",
|
||||||
"typescript": "^4.1.2",
|
"typescript": "^4.1.2",
|
||||||
|
|
15
packages/core/lib/block-helpers.ts
Normal file
15
packages/core/lib/block-helpers.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
export const generateBlockId = uuidv4;
|
||||||
|
|
||||||
|
export const model = {
|
||||||
|
prop: 'block',
|
||||||
|
event: 'update',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const blockProps = {
|
||||||
|
blockId: {
|
||||||
|
type: String,
|
||||||
|
default: generateBlockId,
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,43 +0,0 @@
|
||||||
import { Component } from 'vue';
|
|
||||||
|
|
||||||
export interface BlockTree {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
icon?: string;
|
|
||||||
children?: BlockTree[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BlockDefinition {
|
|
||||||
name: string;
|
|
||||||
icon?: string;
|
|
||||||
getDefaultData: any;
|
|
||||||
edit: Component;
|
|
||||||
display: Component;
|
|
||||||
getChildren?: (block: Block) => Block[],
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BlockLibraryDefinition {
|
|
||||||
[name: string]: BlockDefinition;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BlockProps {
|
|
||||||
id: string;
|
|
||||||
data: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Block extends BlockProps {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const model = {
|
|
||||||
prop: 'block',
|
|
||||||
event: 'update',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const blockProps = {
|
|
||||||
id: {
|
|
||||||
type: String,
|
|
||||||
default: () => `${+(new Date())}`,
|
|
||||||
},
|
|
||||||
data: { type: Object, default: () => ({}) },
|
|
||||||
};
|
|
|
@ -6,37 +6,23 @@ import {
|
||||||
ref,
|
ref,
|
||||||
Ref,
|
Ref,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { Block } from '../blocks';
|
import { BlockData } from '../types';
|
||||||
import { SbMode } from '../mode';
|
import { SbMode } from '../mode';
|
||||||
import { useResizeObserver, BlockDimensions } from '../use-resize-observer';
|
import { useResizeObserver, SymBlockDimensions } from '../use-resize-observer';
|
||||||
import { useActivation } from '../use-activation';
|
import { useActivation } from '../use-activation';
|
||||||
import { useBlockTree } from '../use-block-tree';
|
import { useBlockTree } from '../use-block-tree';
|
||||||
import { useDynamicBlocks } from '../use-dynamic-blocks';
|
import { useDynamicBlocks } from '../use-dynamic-blocks';
|
||||||
|
|
||||||
import { SbBlockOrdering } from './BlockOrdering';
|
import SbMissingBlock from './MissingBlock';
|
||||||
import SbMissingBlock from './BlockMissing/index';
|
|
||||||
|
|
||||||
import './Block.scss';
|
import './Block.scss';
|
||||||
|
|
||||||
interface BlockProps {
|
|
||||||
block: Block;
|
|
||||||
onUpdate: (b?: Block) => void;
|
|
||||||
onPrependBlock: (b?: Block) => void;
|
|
||||||
onAppendBlock: (b?: Block) => void;
|
|
||||||
onRemoveSelf: () => void;
|
|
||||||
onMoveBackward: () => void;
|
|
||||||
onMoveForward: () => void;
|
|
||||||
onActivateNext: () => void;
|
|
||||||
onActivatePrevious: () => void;
|
|
||||||
sortable: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SbBlock = defineComponent({
|
export const SbBlock = defineComponent({
|
||||||
name: 'sb-block',
|
name: 'sb-block',
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
block: {
|
block: {
|
||||||
type: (null as unknown) as PropType<Block>,
|
type: (null as unknown) as PropType<BlockData<any>>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
sortable: {
|
sortable: {
|
||||||
|
@ -47,24 +33,23 @@ export const SbBlock = defineComponent({
|
||||||
onPrependBlock: { type: Function, default: () => {} },
|
onPrependBlock: { type: Function, default: () => {} },
|
||||||
onAppendBlock: { type: Function, default: () => {} },
|
onAppendBlock: { type: Function, default: () => {} },
|
||||||
onRemoveSelf: { type: Function, default: () => {} },
|
onRemoveSelf: { type: Function, default: () => {} },
|
||||||
onMoveBackward: { type: Function, default: () => {} },
|
onActivatePrevious: { type: Function, default: () => {} },
|
||||||
onMoveForward: { type: Function, default: () => {} },
|
onActivateNext: { type: Function, default: () => {} },
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: BlockProps, context) {
|
setup(props, context) {
|
||||||
const el: Ref<null|HTMLElement> = ref(null);
|
const el: Ref<null|HTMLElement> = ref(null);
|
||||||
const { mode, getBlock } = useDynamicBlocks();
|
const { mode, getBlock } = useDynamicBlocks();
|
||||||
const {
|
const {
|
||||||
isActive,
|
isActive,
|
||||||
activate,
|
activate,
|
||||||
isHighlighted,
|
|
||||||
} = useActivation(props.block.id);
|
} = useActivation(props.block.id);
|
||||||
const classes = computed(() => ({
|
const classes = computed(() => ({
|
||||||
'sb-block': true,
|
'sb-block': true,
|
||||||
'sb-block_active': isActive.value,
|
'sb-block_active': isActive.value,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { triggerSizeCalculation } = useResizeObserver(el, BlockDimensions);
|
const { triggerSizeCalculation } = useResizeObserver(el, SymBlockDimensions);
|
||||||
watch(() => props.block.data, triggerSizeCalculation);
|
watch(() => props.block.data, triggerSizeCalculation);
|
||||||
|
|
||||||
const { register } = useBlockTree();
|
const { register } = useBlockTree();
|
||||||
|
@ -114,11 +99,14 @@ export const SbBlock = defineComponent({
|
||||||
onRemoveSelf={props.onRemoveSelf}
|
onRemoveSelf={props.onRemoveSelf}
|
||||||
onActivatePrevious={props.onActivatePrevious}
|
onActivatePrevious={props.onActivatePrevious}
|
||||||
onActivateNext={props.onActivateNext}
|
onActivateNext={props.onActivateNext}
|
||||||
onClick={($event: MouseEvent) => {
|
|
||||||
$event.stopPropagation();
|
{...{
|
||||||
activate();
|
'onClick:value': ($event: MouseEvent) => {
|
||||||
|
$event.stopPropagation();
|
||||||
|
activate();
|
||||||
|
},
|
||||||
|
...context.attrs,
|
||||||
}}
|
}}
|
||||||
{...context.attrs}
|
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,13 +15,13 @@ export const SbBlockOrdering = defineComponent({
|
||||||
name: 'sb-block-ordering',
|
name: 'sb-block-ordering',
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
sortable: {
|
orientation: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
onRemove: { type: Function, default: () => {} },
|
onRemove: { type: Function, default: () => {} },
|
||||||
onMoveUp: { type: Function, default: () => {} },
|
onMoveBackward: { type: Function, default: () => {} },
|
||||||
onMoveDown: { type: Function, default: () => {} },
|
onMoveForward: { type: Function, default: () => {} },
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
@ -32,7 +32,7 @@ export const SbBlockOrdering = defineComponent({
|
||||||
|
|
||||||
const classes = computed(() => ({
|
const classes = computed(() => ({
|
||||||
'sb-block-ordering': true,
|
'sb-block-ordering': true,
|
||||||
[`sb-block-ordering_${props.sortable}`]: !!props.sortable,
|
[`sb-block-ordering_${props.orientation}`]: !!props.orientation,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { editorDimensions, blockDimensions } = useBlockSizing();
|
const { editorDimensions, blockDimensions } = useBlockSizing();
|
||||||
|
@ -47,7 +47,7 @@ export const SbBlockOrdering = defineComponent({
|
||||||
});
|
});
|
||||||
watch(editorDimensions, resetStyles);
|
watch(editorDimensions, resetStyles);
|
||||||
watch(blockDimensions, resetStyles);
|
watch(blockDimensions, resetStyles);
|
||||||
watch(() => props.sortable, resetStyles);
|
watch(() => props.orientation, resetStyles);
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
|
@ -55,9 +55,9 @@ export const SbBlockOrdering = defineComponent({
|
||||||
style={styles}
|
style={styles}
|
||||||
onClick={($event: MouseEvent) => $event.stopPropagation()}
|
onClick={($event: MouseEvent) => $event.stopPropagation()}
|
||||||
>
|
>
|
||||||
<SbButton onClick={props.onMoveUp}>{props.sortable === 'vertical' ? '↑' : '←'}</SbButton>
|
<SbButton {...{onClick: props.onMoveBackward}}>{props.orientation === 'vertical' ? '↑' : '←'}</SbButton>
|
||||||
<SbButton onClick={props.onRemove}>x</SbButton>
|
<SbButton {...{onClick: props.onRemove}}>x</SbButton>
|
||||||
<SbButton onClick={props.onMoveDown}>{props.sortable === 'vertical' ? '↓' : '→'}</SbButton>
|
<SbButton {...{onClick: props.onMoveForward}}>{props.orientation === 'vertical' ? '↓' : '→'}</SbButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,10 +3,8 @@ import {
|
||||||
ref,
|
ref,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import { useDynamicBlocks } from '../use-dynamic-blocks';
|
||||||
useDynamicBlocks,
|
import { BlockDefinition } from '../types';
|
||||||
BlockDefinition,
|
|
||||||
} from '../use-dynamic-blocks';
|
|
||||||
|
|
||||||
import { SbButton } from './Button';
|
import { SbButton } from './Button';
|
||||||
import { SbModal } from './Modal';
|
import { SbModal } from './Modal';
|
||||||
|
@ -16,17 +14,19 @@ import './BlockPicker.scss';
|
||||||
export const SbBlockPicker = defineComponent({
|
export const SbBlockPicker = defineComponent({
|
||||||
name: 'sb-block-picker',
|
name: 'sb-block-picker',
|
||||||
|
|
||||||
props: {},
|
props: {
|
||||||
|
onPickedBlock: { type: Function, default: () => {} },
|
||||||
|
},
|
||||||
|
|
||||||
setup(props, context) {
|
setup(props) {
|
||||||
const open = ref(false);
|
const open = ref(false);
|
||||||
const { customBlocks } = useDynamicBlocks();
|
const { customBlocks } = useDynamicBlocks();
|
||||||
|
|
||||||
const blockList = computed(() => Object.keys(customBlocks).map((key) => customBlocks[key]));
|
const blockList = computed(() => Object.keys(customBlocks).map((key) => customBlocks[key]));
|
||||||
|
|
||||||
const selectBlock = (block: BlockDefinition) => () => {
|
const selectBlock = (block: BlockDefinition<any>) => () => {
|
||||||
open.value = false;
|
open.value = false;
|
||||||
context.emit('picked-block', {
|
props.onPickedBlock({
|
||||||
name: block.name,
|
name: block.name,
|
||||||
id: `${+(new Date())}`,
|
id: `${+(new Date())}`,
|
||||||
data: block.getDefaultData(),
|
data: block.getDefaultData(),
|
||||||
|
@ -37,23 +37,27 @@ export const SbBlockPicker = defineComponent({
|
||||||
<div class="sb-block-picker">
|
<div class="sb-block-picker">
|
||||||
<SbButton
|
<SbButton
|
||||||
class="sb-block-picker__add-button"
|
class="sb-block-picker__add-button"
|
||||||
type="button"
|
{...{
|
||||||
onClick={($event: MouseEvent) => {
|
type: 'button',
|
||||||
open.value = true;
|
onClick: ($event: MouseEvent) => {
|
||||||
$event.stopPropagation();
|
open.value = true;
|
||||||
|
$event.stopPropagation();
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>+</SbButton>
|
>+</SbButton>
|
||||||
<SbModal
|
<SbModal
|
||||||
open={open.value}
|
open={open.value}
|
||||||
onClick={($event: MouseEvent) => $event.stopPropagation()}
|
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
open.value = false;
|
open.value = false;
|
||||||
}}
|
}}
|
||||||
|
{...{ onClick: ($event: MouseEvent) => $event.stopPropagation() }}
|
||||||
>
|
>
|
||||||
{...blockList.value.map((block: BlockDefinition) => (
|
{...blockList.value.map((block: BlockDefinition<any>) => (
|
||||||
<SbButton
|
<SbButton
|
||||||
type="button"
|
{...{
|
||||||
onClick={selectBlock(block)}
|
type: 'button',
|
||||||
|
onClick: () => selectBlock(block),
|
||||||
|
}}
|
||||||
>{block.name}</SbButton>
|
>{block.name}</SbButton>
|
||||||
))}
|
))}
|
||||||
</SbModal>
|
</SbModal>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { BlockDefinition } from '../blocks';
|
import { BlockData } from '../types';
|
||||||
|
|
||||||
import { SbBlockPicker } from './BlockPicker';
|
import { SbBlockPicker } from './BlockPicker';
|
||||||
|
|
||||||
|
@ -8,11 +8,15 @@ import './BlockPlaceholder.scss';
|
||||||
export const SbBlockPlaceholder = defineComponent({
|
export const SbBlockPlaceholder = defineComponent({
|
||||||
name: 'sb-block-placeholder',
|
name: 'sb-block-placeholder',
|
||||||
|
|
||||||
setup(props, context) {
|
props: {
|
||||||
|
onInsertBlock: { type: Function, default: () => {} },
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
return () => (
|
return () => (
|
||||||
<div class="sb-block-placeholder">
|
<div class="sb-block-placeholder">
|
||||||
<SbBlockPicker
|
<SbBlockPicker
|
||||||
onPickedBlock={(block: BlockDefinition) => context.emit('insert-block', block)}
|
onPickedBlock={(block: BlockData<any>) => props.onInsertBlock(block)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,26 +1,11 @@
|
||||||
import {
|
import { defineComponent } from 'vue';
|
||||||
defineComponent,
|
|
||||||
PropType,
|
|
||||||
} from 'vue';
|
|
||||||
import { Block } from '../blocks';
|
|
||||||
|
|
||||||
import './BlockToolbar.scss';
|
import './BlockToolbar.scss';
|
||||||
|
|
||||||
interface BlockToolbarProps {
|
|
||||||
block: Block;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SbBlockToolbar = defineComponent({
|
export const SbBlockToolbar = defineComponent({
|
||||||
name: 'sb-block-toolbar',
|
name: 'sb-block-toolbar',
|
||||||
|
|
||||||
props: {
|
setup() {
|
||||||
block: {
|
|
||||||
type: (null as unknown) as PropType<Block>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props: BlockToolbarProps, context) {
|
|
||||||
return () => (
|
return () => (
|
||||||
<div class="sb-block-toolbar"></div>
|
<div class="sb-block-toolbar"></div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,7 +7,7 @@ export const SbButton = defineComponent({
|
||||||
|
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
|
||||||
setup(props, context) {
|
setup(_, context) {
|
||||||
return () => (
|
return () => (
|
||||||
<button
|
<button
|
||||||
{...{
|
{...{
|
||||||
|
@ -15,7 +15,7 @@ export const SbButton = defineComponent({
|
||||||
class: (context.attrs.class || '') + ' sb-button',
|
class: (context.attrs.class || '') + ' sb-button',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{context.slots.default()}
|
{context.slots.default?.()}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,16 +3,10 @@ import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
ref,
|
ref,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
import { SbButton } from './Button';
|
import { SbButton } from './Button';
|
||||||
|
|
||||||
import './ContextMenu.scss';
|
import './ContextMenu.scss';
|
||||||
|
|
||||||
interface ContextMenuProps {
|
|
||||||
onClose: () => void;
|
|
||||||
onOpen: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SbContextMenu = defineComponent({
|
export const SbContextMenu = defineComponent({
|
||||||
name: 'sb-context-menu',
|
name: 'sb-context-menu',
|
||||||
|
|
||||||
|
@ -21,7 +15,7 @@ export const SbContextMenu = defineComponent({
|
||||||
onOpen: { type: Function, default: () => {} },
|
onOpen: { type: Function, default: () => {} },
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: ContextMenuProps, context) {
|
setup(props, context) {
|
||||||
const opened = ref(false);
|
const opened = ref(false);
|
||||||
const open = () => { opened.value = true; };
|
const open = () => { opened.value = true; };
|
||||||
const close = () => { opened.value = false; };
|
const close = () => { opened.value = false; };
|
||||||
|
@ -40,10 +34,12 @@ export const SbContextMenu = defineComponent({
|
||||||
if (!curr) {
|
if (!curr) {
|
||||||
document.body.removeEventListener('click', close);
|
document.body.removeEventListener('click', close);
|
||||||
document.body.removeEventListener('keypress', closeOnEscape);
|
document.body.removeEventListener('keypress', closeOnEscape);
|
||||||
|
props.onClose();
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.body.addEventListener('click', close);
|
document.body.addEventListener('click', close);
|
||||||
document.body.addEventListener('keypress', closeOnEscape);
|
document.body.addEventListener('keypress', closeOnEscape);
|
||||||
|
props.onOpen();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -51,24 +47,23 @@ export const SbContextMenu = defineComponent({
|
||||||
return () => (
|
return () => (
|
||||||
<div class="sb-context">
|
<div class="sb-context">
|
||||||
{
|
{
|
||||||
context.slots.context({
|
context.slots.context?.({
|
||||||
opened,
|
opened,
|
||||||
toggle,
|
toggle,
|
||||||
close,
|
close,
|
||||||
open,
|
open,
|
||||||
}) ||
|
}) || <SbButton {...{ onClick: toggle }}>Menu</SbButton>
|
||||||
<SbButton onClick={toggle}>Menu</SbButton>
|
|
||||||
}
|
}
|
||||||
<dialog
|
<dialog
|
||||||
class="sb-context-menu"
|
class="sb-context-menu"
|
||||||
open={opened.value ? true : undefined}
|
open={opened.value ? true : undefined}
|
||||||
onClose={close}
|
|
||||||
onClick={($event: Event) => {
|
onClick={($event: Event) => {
|
||||||
// Make sure clicks inside do not autoclose this
|
// Make sure clicks inside do not autoclose this
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
}}
|
}}
|
||||||
|
{...{ onClose: close /* TODO: DialogHTMLAttributes needs an onClose handler type */ }}
|
||||||
>
|
>
|
||||||
{context.slots.default({
|
{context.slots.default?.({
|
||||||
opened,
|
opened,
|
||||||
toggle,
|
toggle,
|
||||||
close,
|
close,
|
||||||
|
|
|
@ -2,32 +2,25 @@ import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
PropType,
|
PropType,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { Block } from '../blocks';
|
import { BlockData } from '../types';
|
||||||
|
|
||||||
import { SbTreeBlockSelect } from './TreeBlockSelect';
|
import { SbTreeBlockSelect } from './TreeBlockSelect';
|
||||||
|
|
||||||
import './MainMenu.scss';
|
import './MainMenu.scss';
|
||||||
|
|
||||||
interface MainMenuProps {
|
|
||||||
block: Block;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SbMainMenu = defineComponent({
|
export const SbMainMenu = defineComponent({
|
||||||
name: 'sb-main-menu',
|
name: 'sb-main-menu',
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
block: {
|
block: {
|
||||||
type: (null as unknown) as PropType<Block>,
|
type: (null as unknown) as PropType<BlockData<any>>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: MainMenuProps, context) {
|
setup() {
|
||||||
return () => (
|
return () => (
|
||||||
<div class="sb-main-menu">
|
<div class="sb-main-menu">
|
||||||
<SbTreeBlockSelect
|
<SbTreeBlockSelect />
|
||||||
block={props.block}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,25 +2,18 @@ import { defineComponent, PropType } from 'vue';
|
||||||
import {
|
import {
|
||||||
model,
|
model,
|
||||||
blockProps,
|
blockProps,
|
||||||
BlockProps,
|
} from '../../block-helpers';
|
||||||
} from '../../blocks';
|
|
||||||
|
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
interface MissingBlockProps extends BlockProps<any> {
|
|
||||||
eventUpdate: (b?: any) => void;
|
|
||||||
eventAppendBlock: (b?: any) => void;
|
|
||||||
eventRemoveBlock: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'sb-missing-block',
|
name: 'sb-missing-block',
|
||||||
|
|
||||||
model,
|
model,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
name: String,
|
|
||||||
...blockProps,
|
...blockProps,
|
||||||
|
name: String,
|
||||||
data: {
|
data: {
|
||||||
type: (null as unknown) as PropType<any>,
|
type: (null as unknown) as PropType<any>,
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -30,11 +23,9 @@ export default defineComponent({
|
||||||
eventRemoveBlock: { type: Function, default: () => {} },
|
eventRemoveBlock: { type: Function, default: () => {} },
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: MissingBlockProps) {
|
setup(props) {
|
||||||
return () => (
|
return () => (
|
||||||
<div class="sb-missing-block">
|
<div class="sb-missing-block">Missing block: {props.name}</div>
|
||||||
Missing block: {props.name}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
|
@ -5,11 +5,6 @@ import {
|
||||||
|
|
||||||
import './Modal.scss';
|
import './Modal.scss';
|
||||||
|
|
||||||
interface ModalProps {
|
|
||||||
open: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SbModal = defineComponent({
|
export const SbModal = defineComponent({
|
||||||
name: 'sb-modal',
|
name: 'sb-modal',
|
||||||
|
|
||||||
|
@ -21,7 +16,7 @@ export const SbModal = defineComponent({
|
||||||
onClose: { type: Function, default: () => {} },
|
onClose: { type: Function, default: () => {} },
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: ModalProps, context) {
|
setup(props, context) {
|
||||||
const classes = computed(() => ({
|
const classes = computed(() => ({
|
||||||
'sb-modal': true,
|
'sb-modal': true,
|
||||||
'sb-modal_open': props.open,
|
'sb-modal_open': props.open,
|
||||||
|
@ -37,7 +32,7 @@ export const SbModal = defineComponent({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="sb-modal__content">
|
<div class="sb-modal__content">
|
||||||
{context.slots.default()}
|
{context.slots.default?.()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,17 +7,21 @@ import {
|
||||||
Ref,
|
Ref,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
model,
|
BlockData,
|
||||||
Block,
|
|
||||||
BlockTree,
|
|
||||||
BlockDefinition,
|
BlockDefinition,
|
||||||
BlockLibraryDefinition,
|
BlockLibrary,
|
||||||
} from '../blocks';
|
TreeNode,
|
||||||
|
} from '../types';
|
||||||
|
import { model } from '../block-helpers';
|
||||||
import { Mode, SbMode } from '../mode';
|
import { Mode, SbMode } from '../mode';
|
||||||
import { BlockLibrary } from '../use-dynamic-blocks';
|
import { SymBlockLibrary} from '../use-dynamic-blocks';
|
||||||
import { BlockTreeSym, BlockTreeRegister, BlockTreeUnregister } from '../use-block-tree';
|
import {
|
||||||
import { EditorDimensions, useResizeObserver } from '../use-resize-observer';
|
SymBlockTree,
|
||||||
import { ActiveBlock } from '../use-activation';
|
SymBlockTreeRegister,
|
||||||
|
SymBlockTreeUnregister,
|
||||||
|
} from '../use-block-tree';
|
||||||
|
import { SymEditorDimensions, useResizeObserver } from '../use-resize-observer';
|
||||||
|
import { SymActiveBlock } from '../use-activation';
|
||||||
|
|
||||||
import { SbMainMenu } from './MainMenu';
|
import { SbMainMenu } from './MainMenu';
|
||||||
import { SbBlockToolbar } from './BlockToolbar';
|
import { SbBlockToolbar } from './BlockToolbar';
|
||||||
|
@ -25,21 +29,20 @@ import { SbBlock } from './Block';
|
||||||
|
|
||||||
import './Schlechtenburg.scss';
|
import './Schlechtenburg.scss';
|
||||||
|
|
||||||
export interface SchlechtenburgProps {
|
export const SbMain = defineComponent({
|
||||||
customBlocks: BlockDefinition[];
|
name: 'sb-main',
|
||||||
onUpdate: (b: Block<any>) => void;
|
|
||||||
block: Block<any>;
|
|
||||||
mode: SbMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Schlechtenburg = defineComponent({
|
|
||||||
name: 'schlechtenburg-main',
|
|
||||||
|
|
||||||
model,
|
model,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
customBlocks: { type: Array as PropType<BlockDefinition[]>, default: () => [] },
|
customBlocks: {
|
||||||
block: { type: Object as PropType<Block<any>>, required: true },
|
type: Array as PropType<BlockDefinition<any>[]>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
block: {
|
||||||
|
type: Object as PropType<BlockData<any>>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
onUpdate: { type: Function, default: () => {} },
|
onUpdate: { type: Function, default: () => {} },
|
||||||
mode: {
|
mode: {
|
||||||
type: String as PropType<SbMode>,
|
type: String as PropType<SbMode>,
|
||||||
|
@ -50,29 +53,29 @@ export const Schlechtenburg = defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: SchlechtenburgProps) {
|
setup(props) { // TODO: why does the typing of props not work here?
|
||||||
const el: Ref<null|HTMLElement> = ref(null);
|
const el: Ref<null|HTMLElement> = ref(null);
|
||||||
useResizeObserver(el, EditorDimensions);
|
useResizeObserver(el, SymEditorDimensions);
|
||||||
|
|
||||||
const mode = ref(props.mode);
|
const mode = ref(props.mode);
|
||||||
provide(Mode, mode);
|
provide(Mode, mode);
|
||||||
|
|
||||||
const activeBlock = ref(null);
|
const activeBlock = ref(null);
|
||||||
provide(ActiveBlock, activeBlock);
|
provide(SymActiveBlock, activeBlock);
|
||||||
|
|
||||||
const blockTree = ref(null);
|
const blockTree: Ref<TreeNode|null> = ref(null);
|
||||||
provide(BlockTreeSym, blockTree);
|
provide(SymBlockTree, blockTree);
|
||||||
provide(BlockTreeRegister, (block: BlockTree) => { blockTree.value = block; });
|
provide(SymBlockTreeRegister, (block: TreeNode) => { blockTree.value = block; });
|
||||||
provide(BlockTreeUnregister, () => { blockTree.value = null; });
|
provide(SymBlockTreeUnregister, () => { blockTree.value = null; });
|
||||||
|
|
||||||
const blockLibrary: BlockLibraryDefinition = shallowReactive({
|
const blockLibrary: BlockLibrary = shallowReactive({
|
||||||
...props.customBlocks.reduce(
|
...props.customBlocks.reduce(
|
||||||
(blocks: {[name: string]: Block<any>}, block: Block<any>) => ({ ...blocks, [block.name]: block }),
|
(blocks: BlockLibrary, block: BlockDefinition<any>) => ({ ...blocks, [block.name]: block }),
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
provide(BlockLibrary, blockLibrary);
|
provide(SymBlockLibrary, blockLibrary);
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
|
@ -83,7 +86,7 @@ export const Schlechtenburg = defineComponent({
|
||||||
mode.value === SbMode.Edit
|
mode.value === SbMode.Edit
|
||||||
? <>
|
? <>
|
||||||
<SbMainMenu block={props.block} />
|
<SbMainMenu block={props.block} />
|
||||||
<SbBlockToolbar block={props.block} />
|
<SbBlockToolbar />
|
||||||
</>
|
</>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,14 @@ export const SbSelect = defineComponent({
|
||||||
|
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
|
||||||
setup(props, context) {
|
setup(_, context) {
|
||||||
return () => (
|
return () => (
|
||||||
<div class="sb-select">
|
<div class="sb-select">
|
||||||
<select
|
<select
|
||||||
class="sb-select__input"
|
class="sb-select__input"
|
||||||
{...context.attrs}
|
{...context.attrs}
|
||||||
>
|
>
|
||||||
{context.slots.default()}
|
{context.slots.default?.()}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import {
|
import { defineComponent } from 'vue';
|
||||||
defineComponent,
|
import { TreeNode } from '../types';
|
||||||
PropType,
|
|
||||||
} from 'vue';
|
|
||||||
import {
|
|
||||||
Block,
|
|
||||||
BlockTree,
|
|
||||||
} from '../blocks';
|
|
||||||
import { useBlockTree } from '../use-block-tree';
|
import { useBlockTree } from '../use-block-tree';
|
||||||
import { useActivation } from '../use-activation';
|
import { useActivation } from '../use-activation';
|
||||||
|
|
||||||
|
@ -14,10 +8,6 @@ import { SbButton } from './Button';
|
||||||
|
|
||||||
import './TreeBlockSelect.scss';
|
import './TreeBlockSelect.scss';
|
||||||
|
|
||||||
interface TreeBlockSelectProps {
|
|
||||||
block: Block;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SbTreeBlockSelect = defineComponent({
|
export const SbTreeBlockSelect = defineComponent({
|
||||||
name: 'sb-main-menu',
|
name: 'sb-main-menu',
|
||||||
|
|
||||||
|
@ -28,7 +18,7 @@ export const SbTreeBlockSelect = defineComponent({
|
||||||
activeBlockId,
|
activeBlockId,
|
||||||
} = useActivation();
|
} = useActivation();
|
||||||
|
|
||||||
const treeToHtml = (tree: BlockTree, close: Function) => <li
|
const treeToHtml = (tree: TreeNode, close: Function) => <li
|
||||||
class={{
|
class={{
|
||||||
'sb-tree-block-select__block': true,
|
'sb-tree-block-select__block': true,
|
||||||
'sb-tree-block-select__block_active': activeBlockId.value === tree.id,
|
'sb-tree-block-select__block_active': activeBlockId.value === tree.id,
|
||||||
|
@ -40,11 +30,11 @@ export const SbTreeBlockSelect = defineComponent({
|
||||||
activate(tree.id);
|
activate(tree.id);
|
||||||
close();
|
close();
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => activate(tree.id)}
|
onMouseenter={() => activate(tree.id)}
|
||||||
>{tree.name}</button>
|
>{tree.name}</button>
|
||||||
{tree.children.length
|
{tree.children?.length
|
||||||
? <ul class="sb-tree-block-select__list">
|
? <ul class="sb-tree-block-select__list">
|
||||||
{tree.children.map((child: BlockTree) => treeToHtml(child, close))}
|
{tree.children?.map((child: TreeNode) => treeToHtml(child, close))}
|
||||||
</ul>
|
</ul>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
@ -55,10 +45,10 @@ export const SbTreeBlockSelect = defineComponent({
|
||||||
? <SbContextMenu
|
? <SbContextMenu
|
||||||
class="sb-tree-block-select"
|
class="sb-tree-block-select"
|
||||||
v-slots={{
|
v-slots={{
|
||||||
context: ({ toggle }) => <SbButton onClick={toggle}>Tree</SbButton>,
|
context: ({ toggle }: { toggle: Function }) => <SbButton {...{ onClick: toggle }}>Tree</SbButton>,
|
||||||
default: ({ close }) => <ul
|
default: ({ close }: { close: Function }) => <ul
|
||||||
class="sb-tree-block-select__list sb-tree-block-select__list_base"
|
class="sb-tree-block-select__list sb-tree-block-select__list_base"
|
||||||
>{treeToHtml(blockTree.value, close)}</ul>,
|
>{treeToHtml(blockTree.value as TreeNode, close)}</ul>,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
: ''
|
: ''
|
||||||
|
|
0
packages/core/lib/id.ts
Normal file
0
packages/core/lib/id.ts
Normal file
|
@ -1,5 +1,7 @@
|
||||||
export * from './mode';
|
export * from './mode';
|
||||||
export * from './blocks';
|
export * from './types';
|
||||||
|
|
||||||
|
export * from './block-helpers';
|
||||||
|
|
||||||
export * from './use-activation';
|
export * from './use-activation';
|
||||||
export * from './use-dynamic-blocks';
|
export * from './use-dynamic-blocks';
|
||||||
|
|
8
packages/core/lib/types.d.ts
vendored
Normal file
8
packages/core/lib/types.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
import {HTMLAttributes} from "vue";
|
||||||
|
|
||||||
|
interface DialogHTMLAttributes extends HTMLAttributes {
|
||||||
|
open?: boolean;
|
||||||
|
onClose?: Function;
|
||||||
|
}
|
||||||
|
*/
|
37
packages/core/lib/types.ts
Normal file
37
packages/core/lib/types.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Component } from 'vue';
|
||||||
|
|
||||||
|
export interface TreeNode {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
children: TreeNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlockData<T> {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
data: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlockProps<T> {
|
||||||
|
blockId: string;
|
||||||
|
data?: T,
|
||||||
|
onUpdate?: (b?: BlockData<T>) => void;
|
||||||
|
onPrependBlock?: (b?: BlockData<T>) => void;
|
||||||
|
onAppendBlock?: (b?: BlockData<T>) => void;
|
||||||
|
onRemoveSelf?: () => void;
|
||||||
|
onActivateNext?: () => void;
|
||||||
|
onActivatePrevious?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlockDefinition<T> {
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
getDefaultData: T;
|
||||||
|
edit: Component<BlockProps<T>>;
|
||||||
|
display: Component<BlockProps<T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlockLibrary {
|
||||||
|
[name: string]: BlockDefinition<any>;
|
||||||
|
}
|
|
@ -5,9 +5,9 @@ import {
|
||||||
computed,
|
computed,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
export const ActiveBlock = Symbol('Schlechtenburg active block');
|
export const SymActiveBlock = Symbol('Schlechtenburg active block');
|
||||||
export function useActivation(currentBlockId?: string) {
|
export function useActivation(currentBlockId: string|null = null) {
|
||||||
const activeBlockId: Ref<string|null> = inject(ActiveBlock, ref(null));
|
const activeBlockId: Ref<string|null> = inject(SymActiveBlock, ref(null));
|
||||||
const isActive = computed(() => activeBlockId.value === currentBlockId);
|
const isActive = computed(() => activeBlockId.value === currentBlockId);
|
||||||
const activate = (id?: string|null) => {
|
const activate = (id?: string|null) => {
|
||||||
activeBlockId.value = id !== undefined ? id : currentBlockId;
|
activeBlockId.value = id !== undefined ? id : currentBlockId;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
ref,
|
||||||
Ref,
|
Ref,
|
||||||
reactive,
|
reactive,
|
||||||
inject,
|
inject,
|
||||||
|
@ -6,19 +7,19 @@ import {
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
BlockTree,
|
TreeNode,
|
||||||
Block,
|
BlockData,
|
||||||
} from './blocks';
|
} from './types';
|
||||||
|
|
||||||
export const BlockTreeSym = Symbol('Schlechtenburg block tree');
|
export const SymBlockTree= Symbol('Schlechtenburg block tree');
|
||||||
export const BlockTreeRegister = Symbol('Schlechtenburg block tree');
|
export const SymBlockTreeRegister = Symbol('Schlechtenburg block tree register');
|
||||||
export const BlockTreeUnregister = Symbol('Schlechtenburg block tree');
|
export const SymBlockTreeUnregister = Symbol('Schlechtenburg block tree unregister');
|
||||||
export function useBlockTree() {
|
export function useBlockTree() {
|
||||||
const blockTree: Ref<BlockTree|null> = inject(BlockTreeSym, null);
|
const blockTree: Ref<TreeNode|null> = inject(SymBlockTree, ref(null));
|
||||||
const registerWithParent = inject(BlockTreeRegister, (_: BlockTree) => {});
|
const registerWithParent = inject(SymBlockTreeRegister, (_: TreeNode) => {});
|
||||||
const unregisterWithParent = inject(BlockTreeUnregister, (_: BlockTree) => {});
|
const unregisterWithParent = inject(SymBlockTreeUnregister, (_: TreeNode) => {});
|
||||||
|
|
||||||
const self: BlockTree= reactive({
|
const self: TreeNode = reactive({
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
icon: '',
|
icon: '',
|
||||||
|
@ -26,8 +27,8 @@ export function useBlockTree() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Provide a registration function to child blocks
|
// Provide a registration function to child blocks
|
||||||
provide(BlockTreeRegister, (block: BlockTree) => {
|
provide(SymBlockTreeRegister, (block: TreeNode) => {
|
||||||
if (self.children.find((child: BlockTree) => child.id === block.id)) {
|
if (self.children.find((child: TreeNode) => child.id === block.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,11 +39,11 @@ export function useBlockTree() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Provide an unregistration function to child blocks
|
// Provide an unregistration function to child blocks
|
||||||
provide(BlockTreeUnregister, ({ id }: BlockTree) => {
|
provide(SymBlockTreeUnregister, ({ id }: TreeNode) => {
|
||||||
self.children = self.children.filter((child: BlockTree) => child.id !== id);
|
self.children = self.children.filter((child: TreeNode) => child.id !== id);
|
||||||
});
|
});
|
||||||
|
|
||||||
const register = (block: Block) => {
|
const register = (block: BlockData<any>) => {
|
||||||
if (!block.id) {
|
if (!block.id) {
|
||||||
throw new Error(`Cannot register a block without an id: ${JSON.stringify(block)}`);
|
throw new Error(`Cannot register a block without an id: ${JSON.stringify(block)}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ import {
|
||||||
inject,
|
inject,
|
||||||
reactive,
|
reactive,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { BlockLibraryDefinition } from './blocks';
|
import { BlockLibrary } from './types';
|
||||||
import { Mode, SbMode } from './mode';
|
import { Mode, SbMode } from './mode';
|
||||||
|
|
||||||
export const BlockLibrary = Symbol('Schlechtenburg block library');
|
export const SymBlockLibrary = Symbol('Schlechtenburg block library');
|
||||||
export function useDynamicBlocks() {
|
export function useDynamicBlocks() {
|
||||||
const mode = inject(Mode, ref(SbMode.Edit));
|
const mode = inject(Mode, ref(SbMode.Edit));
|
||||||
const customBlocks: BlockLibraryDefinition = inject(BlockLibrary, reactive({}));
|
const customBlocks: BlockLibrary = inject(SymBlockLibrary, reactive({}));
|
||||||
const getBlock = (name: string) => customBlocks[name];
|
const getBlock = (name: string) => customBlocks[name];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// <reference types="resize-observer-browser" />
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Ref,
|
Ref,
|
||||||
ref,
|
ref,
|
||||||
|
@ -13,8 +15,8 @@ interface BlockRect {
|
||||||
top: number;
|
top: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BlockDimensions = Symbol('Schlechtenburg block dimensions');
|
export const SymBlockDimensions = Symbol('Schlechtenburg block dimensions');
|
||||||
export const EditorDimensions = Symbol('Schlechtenburg editor dimensions');
|
export const SymEditorDimensions = Symbol('Schlechtenburg editor dimensions');
|
||||||
export function useResizeObserver(el: Ref<null|HTMLElement>, symbol: symbol) {
|
export function useResizeObserver(el: Ref<null|HTMLElement>, symbol: symbol) {
|
||||||
const dimensions: Ref<null|BlockRect> = ref(null);
|
const dimensions: Ref<null|BlockRect> = ref(null);
|
||||||
provide(symbol, dimensions);
|
provide(symbol, dimensions);
|
||||||
|
@ -47,8 +49,8 @@ export function useResizeObserver(el: Ref<null|HTMLElement>, symbol: symbol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useBlockSizing() {
|
export function useBlockSizing() {
|
||||||
const editorDimensions: Ref<BlockRect|null> = inject(EditorDimensions, ref(null));
|
const editorDimensions: Ref<BlockRect|null> = inject(SymEditorDimensions, ref(null));
|
||||||
const blockDimensions: Ref<BlockRect|null> = inject(BlockDimensions, ref(null));
|
const blockDimensions: Ref<BlockRect|null> = inject(SymBlockDimensions, ref(null));
|
||||||
|
|
||||||
return { editorDimensions, blockDimensions };
|
return { editorDimensions, blockDimensions };
|
||||||
}
|
}
|
||||||
|
|
44
packages/core/package-lock.json
generated
44
packages/core/package-lock.json
generated
|
@ -24,6 +24,33 @@
|
||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/lodash": {
|
||||||
|
"version": "4.14.168",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz",
|
||||||
|
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/lodash-es": {
|
||||||
|
"version": "4.17.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.4.tgz",
|
||||||
|
"integrity": "sha512-BBz79DCJbD2CVYZH67MBeHZRX++HF+5p8Mo5MzjZi64Wac39S3diedJYHZtScbRVf4DjZyN6LzA0SB0zy+HSSQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/resize-observer-browser": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-8k/67Z95Goa6Lznuykxkfhq9YU3l1Qe6LNZmwde1u7802a3x8v44oq0j91DICclxatTr0rNnhXx7+VTIetSrSQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/uuid": {
|
||||||
|
"version": "8.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz",
|
||||||
|
"integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@vue/compiler-core": {
|
"@vue/compiler-core": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.4.tgz",
|
||||||
|
@ -88,14 +115,14 @@
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||||
},
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.20",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
"lodash-es": {
|
"lodash-es": {
|
||||||
"version": "4.17.20",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.20.tgz",
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
"integrity": "sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA=="
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||||
},
|
},
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
|
@ -107,6 +134,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||||
},
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||||
|
},
|
||||||
"vue": {
|
"vue": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.4.tgz",
|
||||||
|
|
|
@ -21,7 +21,13 @@
|
||||||
"url": "git@git.b12f.io:b12f/schlechtenburg.git"
|
"url": "git@git.b12f.io:b12f/schlechtenburg.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.0.4",
|
"lodash-es": "^4.17.21",
|
||||||
"lodash-es": "^4.17.20"
|
"uuid": "^8.3.2",
|
||||||
|
"vue": "^3.0.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/lodash-es": "^4.17.4",
|
||||||
|
"@types/resize-observer-browser": "^0.1.5",
|
||||||
|
"@types/uuid": "^8.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
import { getDefaultData } from './util';
|
import { getDefaultData } from './util';
|
||||||
|
|
||||||
|
export * from './util';
|
||||||
|
export const name = 'sb-heading';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'sb-heading',
|
name,
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
edit: defineAsyncComponent(() => import('./edit')),
|
edit: defineAsyncComponent(() => import('./edit')),
|
||||||
display: defineAsyncComponent(() => import('./edit')),
|
display: defineAsyncComponent(() => import('./edit')),
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import {
|
import {
|
||||||
model,
|
model,
|
||||||
blockProps,
|
|
||||||
SbBlock,
|
SbBlock,
|
||||||
} from '@schlechtenburg/core';
|
} from '@schlechtenburg/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
ImageData,
|
ImageData,
|
||||||
ImageProps,
|
|
||||||
} from './util';
|
} from './util';
|
||||||
|
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
@ -19,14 +16,13 @@ export default defineComponent({
|
||||||
model,
|
model,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
...blockProps,
|
|
||||||
data: {
|
data: {
|
||||||
type: (null as unknown) as PropType<ImageData>,
|
type: (null as unknown) as PropType<ImageData>,
|
||||||
default: getDefaultData,
|
default: getDefaultData,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: ImageProps) {
|
setup(props) {
|
||||||
return () => <figure class="sb-image">
|
return () => <figure class="sb-image">
|
||||||
<img
|
<img
|
||||||
class="sb-image__content"
|
class="sb-image__content"
|
||||||
|
|
|
@ -8,16 +8,15 @@ import {
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
model,
|
model,
|
||||||
blockProps,
|
|
||||||
SbToolbar,
|
SbToolbar,
|
||||||
SbButton,
|
SbButton,
|
||||||
SbBlock
|
SbBlock,
|
||||||
|
BlockData,
|
||||||
} from '@schlechtenburg/core';
|
} from '@schlechtenburg/core';
|
||||||
|
import { ParagraphData } from '@schlechtenburg/paragraph';
|
||||||
import {
|
import {
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
ImageData,
|
ImageData,
|
||||||
ImageProps,
|
|
||||||
} from './util';
|
} from './util';
|
||||||
|
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
@ -28,7 +27,6 @@ export default defineComponent({
|
||||||
model,
|
model,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
...blockProps,
|
|
||||||
onUpdate: { type: Function, default: () => {} },
|
onUpdate: { type: Function, default: () => {} },
|
||||||
data: {
|
data: {
|
||||||
type: (null as unknown) as PropType<ImageData>,
|
type: (null as unknown) as PropType<ImageData>,
|
||||||
|
@ -36,7 +34,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: ImageProps) {
|
setup(props) {
|
||||||
const localData = reactive({
|
const localData = reactive({
|
||||||
src: props.data.src,
|
src: props.data.src,
|
||||||
alt: props.data.alt,
|
alt: props.data.alt,
|
||||||
|
@ -61,8 +59,13 @@ export default defineComponent({
|
||||||
if (fileInput.value && fileInput.value.files && fileInput.value.files.length) {
|
if (fileInput.value && fileInput.value.files && fileInput.value.files.length) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.addEventListener('load', () => {
|
reader.addEventListener('load', () => {
|
||||||
|
const src = reader?.result?.toString();
|
||||||
|
if (!src) {
|
||||||
|
throw new Error('Couldn\'t load image src');
|
||||||
|
}
|
||||||
|
|
||||||
props.onUpdate({
|
props.onUpdate({
|
||||||
src: reader.result,
|
src,
|
||||||
alt: props.data.alt,
|
alt: props.data.alt,
|
||||||
description: props.data.description,
|
description: props.data.description,
|
||||||
});
|
});
|
||||||
|
@ -72,7 +75,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDescriptionUpdate = (description) => {
|
const onDescriptionUpdate = (description: BlockData<ParagraphData>) => {
|
||||||
props.onUpdate({
|
props.onUpdate({
|
||||||
...props.data,
|
...props.data,
|
||||||
description,
|
description,
|
||||||
|
@ -83,7 +86,7 @@ export default defineComponent({
|
||||||
<figure class="sb-image">
|
<figure class="sb-image">
|
||||||
<SbToolbar>
|
<SbToolbar>
|
||||||
{localData.src
|
{localData.src
|
||||||
? <SbButton onClick={selectImage}>Change Image</SbButton>
|
? <SbButton {...{ 'onClick:value': selectImage }}>Select Image</SbButton>
|
||||||
: null}
|
: null}
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
|
@ -101,10 +104,11 @@ export default defineComponent({
|
||||||
/>
|
/>
|
||||||
<SbBlock
|
<SbBlock
|
||||||
block={localData.description}
|
block={localData.description}
|
||||||
onUpdate={(updated: Block) => onDescriptionUpdate(updated)}
|
onUpdate={(updated: BlockData<ParagraphData>) => onDescriptionUpdate(updated)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
: <SbButton onClick={selectImage}>Select Image</SbButton>}
|
: <SbButton {...{ 'onClick:value': selectImage }}>Select Image</SbButton>
|
||||||
|
}
|
||||||
</figure>
|
</figure>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
import { getDefaultData } from './util';
|
import { getDefaultData } from './util';
|
||||||
|
|
||||||
|
export * from './util';
|
||||||
|
export const name = 'sb-image';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'sb-image',
|
name,
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
edit: defineAsyncComponent(() => import('./edit')),
|
edit: defineAsyncComponent(() => import('./edit')),
|
||||||
display: defineAsyncComponent(() => import('./display')),
|
display: defineAsyncComponent(() => import('./display')),
|
||||||
getChildren: (block) => [ block.data.description ],
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
import {
|
import {
|
||||||
|
BlockData,
|
||||||
|
generateBlockId,
|
||||||
|
} from '@schlechtenburg/core';
|
||||||
|
import {
|
||||||
|
name as paragraphName,
|
||||||
ParagraphData,
|
ParagraphData,
|
||||||
getDefaultData as getDefaultParagraphData
|
getDefaultData as getDefaultParagraphData
|
||||||
} from '@schlechtenburg/paragraph';
|
} from '@schlechtenburg/paragraph';
|
||||||
import { BlockData, BlockProps } from '/@/blocks';
|
|
||||||
|
|
||||||
export interface ImageData {
|
export interface ImageData {
|
||||||
src: string;
|
src: string;
|
||||||
alt: string;
|
alt: string;
|
||||||
description: ParagraphData;
|
description: BlockData<ParagraphData>;
|
||||||
}
|
|
||||||
|
|
||||||
export interface ImageProps extends BlockProps {
|
|
||||||
data: ImageData;
|
|
||||||
eventUpdate: (b?: BlockData) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDefaultData: () => ImageData = () => ({
|
export const getDefaultData: () => ImageData = () => ({
|
||||||
src: '',
|
src: '',
|
||||||
alt: '',
|
alt: '',
|
||||||
description: getDefaultParagraphData(),
|
description: {
|
||||||
|
id: generateBlockId(),
|
||||||
|
name: paragraphName,
|
||||||
|
data: getDefaultParagraphData(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
5
packages/image/package-lock.json
generated
5
packages/image/package-lock.json
generated
|
@ -102,6 +102,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||||
},
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||||
|
},
|
||||||
"vue": {
|
"vue": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.4.tgz",
|
||||||
|
|
|
@ -5,13 +5,10 @@ import {
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
model,
|
model,
|
||||||
blockProps,
|
|
||||||
SbBlock,
|
SbBlock,
|
||||||
} from '@schlechtenburg/core';
|
} from '@schlechtenburg/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LayoutData,
|
LayoutData,
|
||||||
LayoutProps,
|
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
} from './util';
|
} from './util';
|
||||||
|
|
||||||
|
@ -23,14 +20,13 @@ export default defineComponent({
|
||||||
model,
|
model,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
...blockProps,
|
|
||||||
data: {
|
data: {
|
||||||
type: (null as unknown) as PropType<LayoutData>,
|
type: (null as unknown) as PropType<LayoutData>,
|
||||||
default: getDefaultData,
|
default: getDefaultData,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: LayoutProps) {
|
setup(props) {
|
||||||
const classes = computed(() => ({
|
const classes = computed(() => ({
|
||||||
'sb-layout': true,
|
'sb-layout': true,
|
||||||
[`sb-layout_${props.data.orientation}`]: true,
|
[`sb-layout_${props.data.orientation}`]: true,
|
||||||
|
|
|
@ -7,8 +7,7 @@ import {
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
model,
|
model,
|
||||||
Block,
|
BlockData,
|
||||||
blockProps,
|
|
||||||
useActivation,
|
useActivation,
|
||||||
|
|
||||||
SbBlock,
|
SbBlock,
|
||||||
|
@ -20,7 +19,6 @@ import {
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LayoutData,
|
LayoutData,
|
||||||
LayoutProps,
|
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
} from './util';
|
} from './util';
|
||||||
|
|
||||||
|
@ -32,7 +30,6 @@ export default defineComponent({
|
||||||
model,
|
model,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
...blockProps,
|
|
||||||
onUpdate: { type: Function, default: () => {} },
|
onUpdate: { type: Function, default: () => {} },
|
||||||
data: {
|
data: {
|
||||||
type: (null as unknown) as PropType<LayoutData>,
|
type: (null as unknown) as PropType<LayoutData>,
|
||||||
|
@ -40,8 +37,8 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: LayoutProps) {
|
setup(props) {
|
||||||
const { activate } = useActivation(props.id);
|
const { activate } = useActivation();
|
||||||
|
|
||||||
const localData: LayoutData = reactive({
|
const localData: LayoutData = reactive({
|
||||||
orientation: props.data.orientation,
|
orientation: props.data.orientation,
|
||||||
|
@ -59,13 +56,12 @@ export default defineComponent({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const toggleOrientation = () => {
|
const toggleOrientation = () => {
|
||||||
console.log('toggle');
|
|
||||||
props.onUpdate({
|
props.onUpdate({
|
||||||
orientation: localData.orientation === 'vertical' ? 'horizontal' : 'vertical',
|
orientation: localData.orientation === 'vertical' ? 'horizontal' : 'vertical',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChildUpdate = (child: Block, updated: Block) => {
|
const onChildUpdate = (child: BlockData<any>, updated: BlockData<any>) => {
|
||||||
const index = localData.children.indexOf(child);
|
const index = localData.children.indexOf(child);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
return;
|
return;
|
||||||
|
@ -82,7 +78,7 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const appendBlock = (block: Block) => {
|
const appendBlock = (block: BlockData<any>) => {
|
||||||
localData.children = [
|
localData.children = [
|
||||||
...localData.children,
|
...localData.children,
|
||||||
block,
|
block,
|
||||||
|
@ -91,7 +87,7 @@ export default defineComponent({
|
||||||
activate(block.id);
|
activate(block.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertBlock = (index: number, block: Block) => {
|
const insertBlock = (index: number, block: BlockData<any>) => {
|
||||||
localData.children = [
|
localData.children = [
|
||||||
...localData.children.slice(0, index + 1),
|
...localData.children.slice(0, index + 1),
|
||||||
block,
|
block,
|
||||||
|
@ -162,8 +158,10 @@ export default defineComponent({
|
||||||
<div class={classes.value}>
|
<div class={classes.value}>
|
||||||
<SbToolbar>
|
<SbToolbar>
|
||||||
<SbButton
|
<SbButton
|
||||||
type="button"
|
{...{
|
||||||
onClick={toggleOrientation}
|
type: 'button',
|
||||||
|
onClick: toggleOrientation,
|
||||||
|
}}
|
||||||
>{localData.orientation}</SbButton>
|
>{localData.orientation}</SbButton>
|
||||||
</SbToolbar>
|
</SbToolbar>
|
||||||
|
|
||||||
|
@ -172,12 +170,12 @@ export default defineComponent({
|
||||||
{...{ key: child.id }}
|
{...{ key: child.id }}
|
||||||
data-order={index}
|
data-order={index}
|
||||||
block={child}
|
block={child}
|
||||||
onUpdate={(updated: Block) => onChildUpdate(child, updated)}
|
onUpdate={(updated: BlockData<any>) => onChildUpdate(child, updated)}
|
||||||
onRemoveSelf={() => removeBlock(index)}
|
onRemoveSelf={() => removeBlock(index)}
|
||||||
onPrependBlock={(block: Block) => insertBlock(index - 1, block)}
|
onPrependBlock={(block: BlockData<any>) => insertBlock(index - 1, block)}
|
||||||
onAppendBlock={(block: Block) => insertBlock(index, block)}
|
onAppendBlock={(block: BlockData<any>) => insertBlock(index, block)}
|
||||||
onActivatePrevious={(block: Block) => activateBlock(index - 1,)}
|
onActivatePrevious={() => activateBlock(index - 1,)}
|
||||||
onActivateNext={(block: Block) => activateBlock(index + 1,)}
|
onActivateNext={() => activateBlock(index + 1,)}
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
'context-toolbar': () =>
|
'context-toolbar': () =>
|
||||||
|
@ -185,7 +183,7 @@ export default defineComponent({
|
||||||
onMoveBackward={() => moveBackward(index)}
|
onMoveBackward={() => moveBackward(index)}
|
||||||
onMoveForward={() => moveForward(index)}
|
onMoveForward={() => moveForward(index)}
|
||||||
onRemove={() => removeBlock(index)}
|
onRemove={() => removeBlock(index)}
|
||||||
sortable={props.sortable}
|
orientation={localData.orientation}
|
||||||
/>,
|
/>,
|
||||||
}}
|
}}
|
||||||
</SbBlock>
|
</SbBlock>
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
import { getDefaultData } from './util';
|
import { getDefaultData } from './util';
|
||||||
|
|
||||||
|
export * from './util';
|
||||||
|
export const name = 'sb-layout';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'sb-layout',
|
name,
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
edit: defineAsyncComponent(() => import('./edit')),
|
edit: defineAsyncComponent(() => import('./edit')),
|
||||||
display: defineAsyncComponent(() => import('./display')),
|
display: defineAsyncComponent(() => import('./display')),
|
||||||
|
|
|
@ -1,17 +1,8 @@
|
||||||
import {
|
import { BlockData } from '@schlechtenburg/core';
|
||||||
BlockProps,
|
|
||||||
Block,
|
|
||||||
BlockData,
|
|
||||||
} from '/@/blocks';
|
|
||||||
|
|
||||||
export interface LayoutData {
|
export interface LayoutData {
|
||||||
orientation: string;
|
orientation: string;
|
||||||
children: Block[];
|
children: BlockData<any>[];
|
||||||
}
|
|
||||||
|
|
||||||
export interface LayoutProps extends BlockProps {
|
|
||||||
data: LayoutData;
|
|
||||||
eventUpdate: (b?: BlockData) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDefaultData: () => LayoutData = () => ({
|
export const getDefaultData: () => LayoutData = () => ({
|
||||||
|
|
|
@ -5,10 +5,7 @@ import {
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
model,
|
model,
|
||||||
blockProps,
|
|
||||||
BlockProps,
|
|
||||||
} from '@schlechtenburg/core';
|
} from '@schlechtenburg/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
ParagraphData,
|
ParagraphData,
|
||||||
|
@ -16,24 +13,19 @@ import {
|
||||||
|
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
interface ParagraphProps extends BlockProps {
|
|
||||||
data: ParagraphData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'sb-paragraph-display',
|
name: 'sb-paragraph-display',
|
||||||
|
|
||||||
model,
|
model,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
...blockProps,
|
|
||||||
data: {
|
data: {
|
||||||
type: Object as PropType<ParagraphData>,
|
type: Object as PropType<ParagraphData>,
|
||||||
default: getDefaultData,
|
default: getDefaultData,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: ParagraphProps) {
|
setup(props) {
|
||||||
const classes = computed(() => ({
|
const classes = computed(() => ({
|
||||||
'sb-paragraph': true,
|
'sb-paragraph': true,
|
||||||
[`sb-paragraph_align-${props.data.align}`]: true,
|
[`sb-paragraph_align-${props.data.align}`]: true,
|
||||||
|
|
|
@ -10,15 +10,10 @@ import {
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
model,
|
model,
|
||||||
blockProps,
|
|
||||||
BlockProps,
|
|
||||||
BlockData,
|
|
||||||
useActivation,
|
useActivation,
|
||||||
|
|
||||||
SbToolbar,
|
SbToolbar,
|
||||||
SbSelect,
|
SbSelect,
|
||||||
} from '@schlechtenburg/core';
|
} from '@schlechtenburg/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
ParagraphData,
|
ParagraphData,
|
||||||
|
@ -26,22 +21,13 @@ import {
|
||||||
|
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
interface ParagraphProps extends BlockProps {
|
|
||||||
data: ParagraphData;
|
|
||||||
onUpdate: (b?: ParagraphData) => void;
|
|
||||||
onAppendBlock: (b?: BlockData) => void;
|
|
||||||
onRemoveSelf: () => void;
|
|
||||||
onActivateNext: () => void;
|
|
||||||
onActivatePrevious: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'sb-paragraph-edit',
|
name: 'sb-paragraph-edit',
|
||||||
|
|
||||||
model,
|
model,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
...blockProps,
|
blockId: { type: String, required: true },
|
||||||
data: {
|
data: {
|
||||||
type: (null as unknown) as PropType<ParagraphData>,
|
type: (null as unknown) as PropType<ParagraphData>,
|
||||||
default: getDefaultData,
|
default: getDefaultData,
|
||||||
|
@ -53,7 +39,7 @@ export default defineComponent({
|
||||||
onActivatePrevious: { type: Function, default: () => {} },
|
onActivatePrevious: { type: Function, default: () => {} },
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: ParagraphProps) {
|
setup(props) {
|
||||||
const localData = (reactive({
|
const localData = (reactive({
|
||||||
value: props.data.value,
|
value: props.data.value,
|
||||||
align: props.data.align,
|
align: props.data.align,
|
||||||
|
@ -91,7 +77,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const onTextUpdate = ($event: InputEvent) => {
|
const onTextUpdate = ($event: Event) => {
|
||||||
localData.value = ($event.target as HTMLElement).innerHTML;
|
localData.value = ($event.target as HTMLElement).innerHTML;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,9 +128,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
const node = selection.focusNode;
|
const node = selection?.focusNode;
|
||||||
const childNodes = Array.from(inputEl.value.childNodes);
|
const childNodes = Array.from(inputEl?.value?.childNodes || []);
|
||||||
const index = childNodes.indexOf(node);
|
const index = node ? childNodes.indexOf(node as ChildNode) : -1;
|
||||||
if (node === inputEl.value || index === 0 || index === childNodes.length -1) {
|
if (node === inputEl.value || index === 0 || index === childNodes.length -1) {
|
||||||
switch ($event.key) {
|
switch ($event.key) {
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
|
@ -161,8 +147,10 @@ export default defineComponent({
|
||||||
<div class={classes.value}>
|
<div class={classes.value}>
|
||||||
<SbToolbar>
|
<SbToolbar>
|
||||||
<SbSelect
|
<SbSelect
|
||||||
value={localData.align}
|
{...{
|
||||||
onChange={setAlignment}
|
value: localData.align,
|
||||||
|
onChange: setAlignment,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<option>left</option>
|
<option>left</option>
|
||||||
<option>center</option>
|
<option>center</option>
|
||||||
|
|
|
@ -2,9 +2,10 @@ import { defineAsyncComponent } from 'vue';
|
||||||
import { getDefaultData } from './util';
|
import { getDefaultData } from './util';
|
||||||
|
|
||||||
export * from './util';
|
export * from './util';
|
||||||
|
export const name = 'sb-paragraph';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'sb-paragraph',
|
name,
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
edit: defineAsyncComponent(() => import('./edit')),
|
edit: defineAsyncComponent(() => import('./edit')),
|
||||||
display: defineAsyncComponent(() => import('./display')),
|
display: defineAsyncComponent(() => import('./display')),
|
||||||
|
|
14
src/App.tsx
14
src/App.tsx
|
@ -6,7 +6,7 @@ import {
|
||||||
ref,
|
ref,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
import { Schlechtenburg, Block, SbMode } from '../packages/core/lib';
|
import { SbMain, BlockData, SbMode } from '../packages/core/lib';
|
||||||
|
|
||||||
import SbLayout from '../packages/layout/lib';
|
import SbLayout from '../packages/layout/lib';
|
||||||
import SbHeading from '../packages/heading/lib';
|
import SbHeading from '../packages/heading/lib';
|
||||||
|
@ -20,7 +20,7 @@ export default defineComponent({
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const activeTab = ref('edit');
|
const activeTab = ref('edit');
|
||||||
const block: Block<any> = reactive({
|
const block: BlockData<any> = reactive({
|
||||||
name: 'none',
|
name: 'none',
|
||||||
id: '0',
|
id: '0',
|
||||||
data: null,
|
data: null,
|
||||||
|
@ -34,12 +34,12 @@ export default defineComponent({
|
||||||
block.data = data.data;
|
block.data = data.data;
|
||||||
});
|
});
|
||||||
|
|
||||||
const Example = computed(() => {
|
const displayedElement = computed(() => {
|
||||||
switch (activeTab.value) {
|
switch (activeTab.value) {
|
||||||
case SbMode.Edit:
|
case SbMode.Edit:
|
||||||
return <Schlechtenburg
|
return <SbMain
|
||||||
block={block}
|
block={block}
|
||||||
onUpdate={(newBlock: Block<any>) => {
|
onUpdate={(newBlock: BlockData<any>) => {
|
||||||
block.data = newBlock.data;
|
block.data = newBlock.data;
|
||||||
}}
|
}}
|
||||||
customBlocks={[
|
customBlocks={[
|
||||||
|
@ -52,7 +52,7 @@ export default defineComponent({
|
||||||
mode={SbMode.Edit}
|
mode={SbMode.Edit}
|
||||||
/>;
|
/>;
|
||||||
case SbMode.Display:
|
case SbMode.Display:
|
||||||
return <Schlechtenburg
|
return <SbMain
|
||||||
block={block}
|
block={block}
|
||||||
customBlocks={[
|
customBlocks={[
|
||||||
SbLayout,
|
SbLayout,
|
||||||
|
@ -80,7 +80,7 @@ export default defineComponent({
|
||||||
<option>display</option>
|
<option>display</option>
|
||||||
<option>data</option>
|
<option>data</option>
|
||||||
</select>
|
</select>
|
||||||
<Example.value />
|
{displayedElement.value}
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,5 +12,14 @@
|
||||||
// "noErrorTruncation": true,
|
// "noErrorTruncation": true,
|
||||||
"paths": {}
|
"paths": {}
|
||||||
},
|
},
|
||||||
"include": [ "lib/**/*.ts", "lib/**/*.d.ts", "lib/**/*.tsx", "lib/**/*.vue" ]
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue",
|
||||||
|
"packages/**/lib/**/*.ts",
|
||||||
|
"packages/**lib/**/*.d.ts",
|
||||||
|
"packages/**lib/**/*.tsx",
|
||||||
|
"packages/**lib/**/*.vue"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
base: '/schlechtenburg/',
|
base: './',
|
||||||
plugins: [
|
plugins: [
|
||||||
vueJsx({}),
|
vueJsx({}),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Add table
Reference in a new issue