Remove everything unnecessary for minimal reproducation
This commit is contained in:
parent
3f93f77602
commit
88dcf940b8
|
@ -1 +0,0 @@
|
||||||
.sb-missing-block{flex-basis:100%}
|
|
|
@ -1,2 +0,0 @@
|
||||||
import{y as a,m as s,z as e,D as t,e as l}from"./index.a48301fd.js";/* empty css */import"./vendor.a029424f.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;
|
|
||||||
//# sourceMappingURL=display.54a23f84.js.map
|
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"display.54a23f84.js","sources":["../../packages/image/lib/display.tsx"],"sourcesContent":["import { defineComponent, PropType } from 'vue';\nimport {\n model,\n SbBlock,\n} from '@schlechtenburg/core';\nimport {\n getDefaultData,\n ImageData,\n} from './util';\n\nimport './style.scss';\n\nexport default defineComponent({\n name: 'sb-image-display',\n\n model,\n\n props: {\n data: {\n type: (null as unknown) as PropType<ImageData>,\n default: getDefaultData,\n },\n },\n\n setup(props) {\n return () => <figure class=\"sb-image\">\n <img\n class=\"sb-image__content\"\n src={props.data.src}\n alt={props.data.alt}\n />\n <SbBlock block={props.data.description} />\n </figure>;\n },\n});\n"],"names":["defineComponent","name","model","props","data","type","default","getDefaultData","setup","src","alt","description"],"mappings":"6HAYA,MAAeA,EAAgB,CAC7BC,KAAM,mBAENC,MAAAA,EAEAC,MAAO,CACLC,KAAM,CACJC,KAAO,KACPC,QAASC,IAIbC,MAAML,GACG,sBAAoB,4BAEf,wBACDA,EAAMC,KAAKK,QACXN,EAAMC,KAAKM,sBAEFP,EAAMC,KAAKO"}
|
|
|
@ -1,2 +0,0 @@
|
||||||
var e=Object.assign;import{G as t,m as a,H as n,I as s,J as l}from"./index.a48301fd.js";import"./vendor.a029424f.js";var o=t({name:"sb-missing-block",model:a,props:e(e({},n),{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;
|
|
||||||
//# sourceMappingURL=display.82dfc9cb.js.map
|
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"display.82dfc9cb.js","sources":["../../packages/core/lib/components/MissingBlock/display.tsx"],"sourcesContent":["import { defineComponent, PropType } from 'vue';\nimport {\n model,\n blockProps,\n} from '../../block-helpers';\n\nimport './style.scss';\n\nexport default defineComponent({\n name: 'sb-missing-block',\n\n model,\n\n props: {\n ...blockProps,\n name: String,\n data: {\n type: (null as unknown) as PropType<any>,\n default: null,\n },\n eventUpdate: { type: Function, default: () => {} },\n eventAppendBlock: { type: Function, default: () => {} },\n eventRemoveBlock: { type: Function, default: () => {} },\n },\n\n setup(props) {\n return () => (\n <div class=\"sb-missing-block\">Missing block: {props.name}</div>\n );\n },\n});\n"],"names":["defineComponent","name","model","props","__assign","blockProps","String","data","type","default","eventUpdate","Function","eventAppendBlock","eventRemoveBlock","setup"],"mappings":"2HAQeA,EAAgB,CAC7BC,KAAM,mBAENC,MAAAA,EAEAC,MAAOC,OACFC,GADE,CAELJ,KAAMK,OACNC,KAAM,CACJC,KAAO,KACPC,QAAS,MAEXC,YAAa,CAAEF,KAAMG,SAAUF,QAAS,QACxCG,iBAAkB,CAAEJ,KAAMG,SAAUF,QAAS,QAC7CI,iBAAkB,CAAEL,KAAMG,SAAUF,QAAS,UAG/CK,MAAMX,GACG,mBACM,0CAAmCA,EAAMF"}
|
|
|
@ -1,2 +0,0 @@
|
||||||
import{d as a,m as s,g as t,c as e,a as l,e as o}from"./index.a48301fd.js";/* empty css */import"./vendor.a029424f.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;
|
|
||||||
//# sourceMappingURL=display.a9bb8ea2.js.map
|
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"display.a9bb8ea2.js","sources":["../../packages/layout/lib/display.tsx"],"sourcesContent":["import {\n defineComponent,\n computed,\n PropType,\n} from 'vue';\nimport {\n model,\n SbBlock,\n} from '@schlechtenburg/core';\nimport {\n LayoutData,\n getDefaultData,\n} from './util';\n\nimport './style.scss';\n\nexport default defineComponent({\n name: 'sb-layout-display',\n\n model,\n\n props: {\n data: {\n type: (null as unknown) as PropType<LayoutData>,\n default: getDefaultData,\n },\n },\n\n setup(props) {\n const classes = computed(() => ({\n 'sb-layout': true,\n [`sb-layout_${props.data.orientation}`]: true,\n }));\n\n return () => (\n <div class={classes.value}>\n {...props.data.children.map((child) => (\n <SbBlock\n key={child.id}\n block={child}\n />\n ))}\n </div>\n );\n },\n});\n"],"names":["defineComponent","name","model","props","data","type","default","getDefaultData","setup","classes","computed","orientation","value","children","map","child","id"],"mappings":"oIAgBA,MAAeA,EAAgB,CAC7BC,KAAM,oBAENC,MAAAA,EAEAC,MAAO,CACLC,KAAM,CACJC,KAAO,KACPC,QAASC,IAIbC,MAAML,SACEM,EAAUC,GAAS,KAAO,cACjB,GACX,aAAYP,EAAMC,KAAKO,gBAAgB,YAGpC,mBACOF,EAAQG,WACdT,EAAMC,KAAKS,SAASC,iBAEfC,EAAMC,SACJD"}
|
|
|
@ -1,2 +0,0 @@
|
||||||
import{j as a,m as s,k as r,q as p,s as e,x as t}from"./index.a48301fd.js";/* empty css */import"./vendor.a029424f.js";var l=a({name:"sb-paragraph-display",model:s,props:{data:{type:Object,default:r}},setup(a){const s=p((()=>({"sb-paragraph":!0,[`sb-paragraph_align-${a.data.align}`]:!0})));return()=>e("p",t({class:s.value},{innerHTML:a.data.value}),null)}});export default l;
|
|
||||||
//# sourceMappingURL=display.cf8b58ad.js.map
|
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"display.cf8b58ad.js","sources":["../../packages/paragraph/lib/display.tsx"],"sourcesContent":["import {\n defineComponent,\n computed,\n PropType,\n} from 'vue';\nimport {\n model,\n} from '@schlechtenburg/core';\nimport {\n getDefaultData,\n ParagraphData,\n} from './util';\n\nimport './style.scss';\n\nexport default defineComponent({\n name: 'sb-paragraph-display',\n\n model,\n\n props: {\n data: {\n type: Object as PropType<ParagraphData>,\n default: getDefaultData,\n },\n },\n\n setup(props) {\n const classes = computed(() => ({\n 'sb-paragraph': true,\n [`sb-paragraph_align-${props.data.align}`]: true,\n }));\n\n return () => <p\n class={classes.value}\n {...{\n innerHTML: props.data.value,\n }}\n ></p>;\n },\n});\n"],"names":["defineComponent","name","model","props","data","type","Object","default","getDefaultData","setup","classes","computed","align","value","innerHTML"],"mappings":"oIAeA,MAAeA,EAAgB,CAC7BC,KAAM,uBAENC,MAAAA,EAEAC,MAAO,CACLC,KAAM,CACJC,KAAMC,OACNC,QAASC,IAIbC,MAAMN,SACEO,EAAUC,GAAS,KAAO,iBACd,GACd,sBAAqBR,EAAMC,KAAKQ,UAAU,YAGvC,mBACEF,EAAQG,QAEbC,UAAWX,EAAMC,KAAKS"}
|
|
2
docs/assets/edit.2536c400.js
Normal file
2
docs/assets/edit.2536c400.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import{d as a,g as e,r,a as s,o as l,c as p,b as t}from"./index.863ba482.js";import"./vendor.41d82814.js";var n=a({name:"sb-paragraph-edit",model:{prop:"block",event:"update"},props:{blockId:{type:String,required:!0},data:{type:null,default:e}},setup(a){const e=r({value:a.data.value,align:a.data.align,focused:!1}),n=s(null);l((()=>{n.value&&(n.value.innerHTML=e.value)}));const d=p((()=>({"sb-paragraph":!0,"sb-paragraph_focused":e.focused,[`sb-paragraph_align-${e.align}`]:!0})));return()=>t("div",{class:d.value},[t("p",{class:"sb-paragraph__input",ref:n,contenteditable:!0},null)])}});export default n;
|
||||||
|
//# sourceMappingURL=edit.2536c400.js.map
|
1
docs/assets/edit.2536c400.js.map
Normal file
1
docs/assets/edit.2536c400.js.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"edit.2536c400.js","sources":["../../packages/paragraph/lib/edit.tsx"],"sourcesContent":["import {\n defineComponent,\n reactive,\n computed,\n ref,\n Ref,\n onMounted,\n PropType,\n} from 'vue';\nimport {\n getDefaultData,\n ParagraphData,\n} from './util';\n\nimport './style.scss';\n\nexport default defineComponent({\n name: 'sb-paragraph-edit',\n\n model: {\n prop: 'block',\n event: 'update',\n },\n\n props: {\n blockId: { type: String, required: true },\n data: {\n type: (null as unknown) as PropType<ParagraphData>,\n default: getDefaultData,\n },\n },\n\n setup(props) {\n const localData = (reactive({\n value: props.data.value,\n align: props.data.align,\n focused: false,\n }) as unknown) as {\n value: string;\n align: string;\n focused: boolean;\n };\n\n const inputEl: Ref<null|HTMLElement> = ref(null);\n onMounted(() => {\n if (inputEl.value) {\n inputEl.value.innerHTML = localData.value;\n }\n });\n\n const classes = computed(() => ({\n 'sb-paragraph': true,\n 'sb-paragraph_focused': localData.focused,\n [`sb-paragraph_align-${localData.align}`]: true,\n }));\n\n return () => (\n <div class={classes.value}>\n <p\n class=\"sb-paragraph__input\"\n ref={inputEl}\n contenteditable\n ></p>\n </div>\n );\n },\n});\n"],"names":["defineComponent","name","model","prop","event","props","blockId","type","String","required","data","default","getDefaultData","setup","localData","reactive","value","align","focused","inputEl","ref","innerHTML","classes","computed"],"mappings":"gHAgBeA,EAAgB,CAC7BC,KAAM,oBAENC,MAAO,CACLC,KAAM,QACNC,MAAO,UAGTC,MAAO,CACLC,QAAS,CAAEC,KAAMC,OAAQC,UAAU,GACnCC,KAAM,CACJH,KAAO,KACPI,QAASC,IAIbC,MAAMR,SACES,EAAaC,EAAS,CAC1BC,MAAOX,EAAMK,KAAKM,MAClBC,MAAOZ,EAAMK,KAAKO,MAClBC,SAAS,IAOLC,EAAiCC,EAAI,SACjC,KACJD,EAAQH,UACFA,MAAMK,UAAYP,EAAUE,gBAIlCM,EAAUC,GAAS,KAAO,iBACd,yBACQT,EAAUI,SAChC,sBAAqBJ,EAAUG,UAAU,YAGtC,mBACOK,EAAQN,qBAEV,0BACDG"}
|
|
@ -1,2 +0,0 @@
|
||||||
var a=Object.assign;import{y as t,m as e,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.a48301fd.js";/* empty css */import"./vendor.a029424f.js";var f=t({name:"sb-image-edit",model:e,props:{onUpdate:{type:Function,default:()=>{}},data:{type:null,default:l}},setup(t){const e=s({src:t.data.src,alt:t.data.alt,description:t.data.description}),l=n(null);i((()=>t.data),(()=>{e.src=t.data.src,e.alt=t.data.alt,e.description=t.data.description}));const f=()=>{l.value&&l.value.click()},v=()=>{if(l.value&&l.value.files&&l.value.files.length){const a=new FileReader;a.addEventListener("load",(()=>{var e;const l=null==(e=null==a?void 0:a.result)?void 0:e.toString();if(!l)throw new Error("Couldn't load image src");t.onUpdate({src:l,alt:t.data.alt,description:t.data.description})})),a.readAsDataURL(l.value.files[0])}};return()=>d("figure",{class:"sb-image"},[d(r,null,{default:()=>[e.src?d(o,{onClick:f},{default:()=>[c("Select Image")]}):null,d("input",{type:"file",ref:l,style:"display: none;",onInput:v},null)]}),e.src?d(u,null,[d("img",{src:e.src,alt:e.alt,class:"sb-image__content"},null),d(p,{block:e.description,onUpdate:e=>{return l=e,void t.onUpdate(a(a({},t.data),{description:l}));var l}},null)]):d(o,{onClick:f},{default:()=>[c("Select Image")]})])}});export default f;
|
|
||||||
//# sourceMappingURL=edit.79e9c821.js.map
|
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"edit.79e9c821.js","sources":["../../packages/image/lib/edit.tsx"],"sourcesContent":["import {\n defineComponent,\n reactive,\n ref,\n Ref,\n watch,\n PropType,\n} from 'vue';\nimport {\n model,\n SbToolbar,\n SbButton,\n SbBlock,\n BlockData,\n} from '@schlechtenburg/core';\nimport { ParagraphData } from '@schlechtenburg/paragraph';\nimport {\n getDefaultData,\n ImageData,\n} from './util';\n\nimport './style.scss';\n\nexport default defineComponent({\n name: 'sb-image-edit',\n\n model,\n\n props: {\n onUpdate: { type: Function, default: () => {} },\n data: {\n type: (null as unknown) as PropType<ImageData>,\n default: getDefaultData,\n },\n },\n\n setup(props) {\n const localData = reactive({\n src: props.data.src,\n alt: props.data.alt,\n description: props.data.description,\n });\n\n const fileInput: Ref<null|HTMLInputElement> = ref(null);\n\n watch(() => props.data, () => {\n localData.src = props.data.src;\n localData.alt = props.data.alt;\n localData.description = props.data.description;\n });\n\n const selectImage = () => {\n if (fileInput.value) {\n fileInput.value.click();\n }\n };\n\n const onImageSelect = () => {\n if (fileInput.value && fileInput.value.files && fileInput.value.files.length) {\n const reader = new FileReader();\n reader.addEventListener('load', () => {\n const src = reader?.result?.toString();\n if (!src) {\n throw new Error('Couldn\\'t load image src');\n }\n\n props.onUpdate({\n src,\n alt: props.data.alt,\n description: props.data.description,\n });\n });\n\n reader.readAsDataURL(fileInput.value.files[0]);\n }\n };\n\n const onDescriptionUpdate = (description: BlockData<ParagraphData>) => {\n props.onUpdate({\n ...props.data,\n description,\n });\n };\n\n return () => (\n <figure class=\"sb-image\">\n <SbToolbar>\n {localData.src\n ? <SbButton {...{ onClick: selectImage }}>Select Image</SbButton>\n : null}\n <input\n type=\"file\"\n ref={fileInput}\n style=\"display: none;\"\n onInput={onImageSelect}\n />\n </SbToolbar>\n {localData.src\n ? <>\n <img\n src={localData.src}\n alt={localData.alt}\n class=\"sb-image__content\"\n />\n <SbBlock\n block={localData.description}\n onUpdate={(updated: BlockData<ParagraphData>) => onDescriptionUpdate(updated)}\n />\n </>\n : <SbButton {...{ onClick: selectImage }}>Select Image</SbButton>\n }\n </figure>\n );\n },\n});\n"],"names":["defineComponent","name","model","props","onUpdate","type","Function","default","data","getDefaultData","setup","localData","reactive","src","alt","description","fileInput","ref","selectImage","value","click","onImageSelect","files","length","reader","FileReader","addEventListener","result","toString","Error","readAsDataURL","onClick","onDescriptionUpdate","updated","__assign"],"mappings":"kMAuBA,MAAeA,EAAgB,CAC7BC,KAAM,gBAENC,MAAAA,EAEAC,MAAO,CACLC,SAAU,CAAEC,KAAMC,SAAUC,QAAS,QACrCC,KAAM,CACJH,KAAO,KACPE,QAASE,IAIbC,MAAMP,SACEQ,EAAYC,EAAS,CACzBC,IAAKV,EAAMK,KAAKK,IAChBC,IAAKX,EAAMK,KAAKM,IAChBC,YAAaZ,EAAMK,KAAKO,cAGpBC,EAAwCC,EAAI,SAE5C,IAAMd,EAAMK,OAAM,OACZK,IAAMV,EAAMK,KAAKK,MACjBC,IAAMX,EAAMK,KAAKM,MACjBC,YAAcZ,EAAMK,KAAKO,qBAG/BG,EAAc,KACdF,EAAUG,SACFA,MAAMC,SAIdC,EAAgB,QAChBL,EAAUG,OAASH,EAAUG,MAAMG,OAASN,EAAUG,MAAMG,MAAMC,OAAQ,OACtEC,EAAS,IAAIC,aACZC,iBAAiB,QAAQ,iBACxBb,EAAMW,0BAAQG,iBAAQC,eACvBf,QACG,IAAIgB,MAAM,6BAGZzB,SAAS,CACbS,IAAAA,EACAC,IAAKX,EAAMK,KAAKM,IAChBC,YAAaZ,EAAMK,KAAKO,mBAIrBe,cAAcd,EAAUG,MAAMG,MAAM,YAWxC,sBACS,oCAETX,EAAUE,SACSkB,QAASb,sCACzB,qBAEG,WACAF,QACC,yBACGK,YAGZV,EAAUE,2BAGEF,EAAUE,QACVF,EAAUG,UACT,sCAGCH,EAAUI,yBACgCiB,SAAoBC,SA5BzE7B,SAAS8B,OACV/B,EAAMK,MADI,CAEbO,YAAAA,KAHyBA,qBAgCLgB,QAASb"}
|
|
|
@ -1,2 +0,0 @@
|
||||||
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 o,S as c,b as h,e as s,f as p,h as u,i as v}from"./index.a48301fd.js";/* empty css */import"./vendor.a029424f.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"})},U=e=>{t.children=[...t.children,e],n.onUpdate({children:[...t.children]}),i(e.id)},b=(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()=>o("div",{class:f.value},[o(c,null,{default:()=>[o(h,{type:"button",onClick:m},{default:()=>[t.orientation]})]}),...t.children.map(((i,l)=>o(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=>b(l-1,e),onAppendBlock:e=>b(l,e),onActivatePrevious:()=>k(l-1),onActivateNext:()=>k(l+1)}),{"context-toolbar":()=>o(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)}))),o(v,{onInsertBlock:U},null)])}});export default f;
|
|
||||||
//# sourceMappingURL=edit.85b1500c.js.map
|
|
File diff suppressed because one or more lines are too long
|
@ -1,2 +0,0 @@
|
||||||
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.a48301fd.js";/* empty css */import"./vendor.a029424f.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),y=()=>{c.value&&f.value&&c.value.focus()};u((()=>{y(),c.value&&(c.value.innerHTML=e.value)})),i(f,y),i((()=>a.data),(()=>{e.value=a.data.value,e.align=a.data.align,c.value&&(c.value.innerHTML=e.value)}));const h=a=>{e.value=a.target.innerHTML},b=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:b.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:h,onFocus:k,onBlur:A,onKeydown:w,onKeyup:F},null)])}});export default c;
|
|
||||||
//# sourceMappingURL=edit.b0a76e09.js.map
|
|
File diff suppressed because one or more lines are too long
|
@ -1,2 +0,0 @@
|
||||||
export default{};
|
|
||||||
//# sourceMappingURL=edit.bd5075df.js.map
|
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"edit.bd5075df.js","sources":["../../packages/heading/lib/edit.tsx"],"sourcesContent":["export default {};\n"],"names":[],"mappings":"cAAe"}
|
|
|
@ -1 +0,0 @@
|
||||||
@charset "UTF-8";.sb-button{border:0;padding:8px 12px;background-color:var(--grey-0);border:1px solid var(--grey-2)}.sb-button:hover{border:1px solid var(--interact)}.sb-context{position:relative}.sb-context-menu{display:none;flex-direction:column;background:var(--grey-0);border:1px solid var(--grey-3);top:100%;left:0;margin:0;z-index:var(--z-context-menu);max-height:70vh;max-width:100vw;overflow:auto}.sb-context-menu[open]{display:flex}.sb-tree-block-select__list{list-style:none;margin:0;padding:0}.sb-tree-block-select__list_base{padding-right:1rem}.sb-tree-block-select__block{padding:0;margin:0;padding-left:1rem}.sb-tree-block-select__block-name{display:block;background:0 0;border:0;font:inherit;color:inherit;padding:.5rem 1rem;width:100%;text-align:left}.sb-tree-block-select__block_active>.sb-tree-block-select__block-name{outline:1px solid var(--interact)}.sb-block{display:flex;align-items:stretch;justify-items:stretch;height:auto}.sb-block>*>.sb-toolbar{opacity:0;pointer-events:none}.sb-block>.sb-block-ordering{opacity:0;pointer-events:none}.sb-block_active{outline:4px solid var(--interact)}.sb-block_active>*>.sb-toolbar{opacity:1;pointer-events:all;outline:1px solid var(--grey-2)}.sb-block_active>.sb-block-ordering{opacity:1;pointer-events:all}.sb-block_highlighted{outline:2px solid var(--interact)}.sb-main{position:relative;background-color:var(--bg);padding:50px 40px}.sb-modal__overlay{background-color:var(--grey-3-t);position:fixed;z-index:10;top:0;left:0;bottom:0;right:0;padding:10vh 10vw;display:flex;justify-content:center;align-items:center;opacity:0;pointer-events:none}.sb-modal__content{width:900px;max-width:100%;height:auto;max-height:100%;background-color:var(--grey-0);padding:24px 32px}.sb-modal_open .sb-modal__overlay{opacity:1;pointer-events:all}.sb-block-picker{display:flex;justify-content:center;align-items:center;height:100%;width:100%}.sb-block-picker__add-button{padding:24px 32px}.sb-block-ordering{display:flex;position:absolute;flex-direction:column}.sb-block-placeholder{width:100%;position:relative;overflow:visible}.sb-block-placeholder__add{background-color:var(--grey-1);width:100%}.sb-toolbar{position:absolute;width:auto;height:auto}.sb-select{background-color:var(--grey-0);border:1px solid var(--grey-2);position:relative}.sb-select:hover{border:1px solid var(--interact)}.sb-select::after{position:absolute;content:"⯆";top:6px;height:100%;right:12px;pointer-events:none}.sb-select__input{background:0 0;appearance:none;border:0;padding:8px 32px 8px 12px}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#2c3e50}*,::after,::before{box-sizing:border-box}html{--grey-0:white;--grey-1-t:rgba(0, 0, 0, 0.05);--grey-1:rgb(242, 242, 242);--grey-2-t:rgba(0, 0, 0, 0.1);--grey-2:rgb(230, 230, 230);--grey-3-t:rgba(0, 0, 0, 0.2);--grey-3:rgb(205, 205, 205);--grey-4-t:rgba(0, 0, 0, 0.4);--grey-4:rgb(155, 155, 155);--grey-5-t:rgba(0, 0, 0, 0.7);--grey-5:rgb(75, 75, 75);--black:rgba(0, 0, 0, 0.9);--bg:var(--grey-1);--fg:var(--black);--interact:#3f9cff;--z-context-menu:3000}body{margin:0;min-height:100vh}
|
|
2
docs/assets/index.863ba482.js
Normal file
2
docs/assets/index.863ba482.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/assets/index.863ba482.js.map
Normal file
1
docs/assets/index.863ba482.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/index.e3cfcab8.css
Normal file
1
docs/assets/index.e3cfcab8.css
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.sb-block{display:flex;align-items:stretch;justify-items:stretch;height:auto}.sb-block>*>.sb-toolbar{opacity:0;pointer-events:none}.sb-block>.sb-block-ordering{opacity:0;pointer-events:none}.sb-block_active{outline:4px solid var(--interact)}.sb-block_active>*>.sb-toolbar{opacity:1;pointer-events:all;outline:1px solid var(--grey-2)}.sb-block_active>.sb-block-ordering{opacity:1;pointer-events:all}.sb-block_highlighted{outline:2px solid var(--interact)}.sb-main{position:relative;background-color:var(--bg);padding:50px 40px}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#2c3e50}*,::after,::before{box-sizing:border-box}html{--grey-0:white;--grey-1-t:rgba(0, 0, 0, 0.05);--grey-1:rgb(242, 242, 242);--grey-2-t:rgba(0, 0, 0, 0.1);--grey-2:rgb(230, 230, 230);--grey-3-t:rgba(0, 0, 0, 0.2);--grey-3:rgb(205, 205, 205);--grey-4-t:rgba(0, 0, 0, 0.4);--grey-4:rgb(155, 155, 155);--grey-5-t:rgba(0, 0, 0, 0.7);--grey-5:rgb(75, 75, 75);--black:rgba(0, 0, 0, 0.9);--bg:var(--grey-1);--fg:var(--black);--interact:#3f9cff;--z-context-menu:3000}body{margin:0;min-height:100vh}
|
|
@ -1 +0,0 @@
|
||||||
.sb-layout{display:flex}.sb-layout_vertical{flex-direction:column}.sb-layout_horizontal{flex-direction:row}.sb-layout__item{position:relative}.sb-layout>*{flex-basis:auto;flex-grow:1;flex-shrink:1}
|
|
|
@ -1 +0,0 @@
|
||||||
.sb-image{margin:0}.sb-image__content{width:100%;height:auto}
|
|
2
docs/assets/vendor.41d82814.js
Normal file
2
docs/assets/vendor.41d82814.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/assets/vendor.41d82814.js.map
Normal file
1
docs/assets/vendor.41d82814.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -5,9 +5,9 @@
|
||||||
<link rel="icon" href="./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="./assets/index.a48301fd.js"></script>
|
<script type="module" crossorigin src="./assets/index.863ba482.js"></script>
|
||||||
<link rel="modulepreload" href="./assets/vendor.a029424f.js">
|
<link rel="modulepreload" href="./assets/vendor.41d82814.js">
|
||||||
<link rel="stylesheet" href="./assets/index.62d1366f.css">
|
<link rel="stylesheet" href="./assets/index.e3cfcab8.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
File diff suppressed because one or more lines are too long
242
packages/core/lib/ResizeObserver.d.ts
vendored
242
packages/core/lib/ResizeObserver.d.ts
vendored
|
@ -1,242 +0,0 @@
|
||||||
/**
|
|
||||||
* The **ResizeObserver** interface reports changes to the dimensions of an
|
|
||||||
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)'s content
|
|
||||||
* or border box, or the bounding box of an
|
|
||||||
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
|
|
||||||
*
|
|
||||||
* > **Note**: The content box is the box in which content can be placed,
|
|
||||||
* > meaning the border box minus the padding and border width. The border box
|
|
||||||
* > encompasses the content, padding, and border. See
|
|
||||||
* > [The box model](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model)
|
|
||||||
* > for further explanation.
|
|
||||||
*
|
|
||||||
* `ResizeObserver` avoids infinite callback loops and cyclic dependencies that
|
|
||||||
* are often created when resizing via a callback function. It does this by only
|
|
||||||
* processing elements deeper in the DOM in subsequent frames. Implementations
|
|
||||||
* should, if they follow the specification, invoke resize events before paint
|
|
||||||
* and after layout.
|
|
||||||
*
|
|
||||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
|
|
||||||
*/
|
|
||||||
declare class ResizeObserver {
|
|
||||||
/**
|
|
||||||
* The **ResizeObserver** constructor creates a new `ResizeObserver` object,
|
|
||||||
* which can be used to report changes to the content or border box of an
|
|
||||||
* `Element` or the bounding box of an `SVGElement`.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* var ResizeObserver = new ResizeObserver(callback)
|
|
||||||
*
|
|
||||||
* @param callback
|
|
||||||
* The function called whenever an observed resize occurs. The function is
|
|
||||||
* called with two parameters:
|
|
||||||
* * **entries**
|
|
||||||
* An array of
|
|
||||||
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
|
|
||||||
* objects that can be used to access the new dimensions of the element
|
|
||||||
* after each change.
|
|
||||||
* * **observer**
|
|
||||||
* A reference to the `ResizeObserver` itself, so it will definitely be
|
|
||||||
* accessible from inside the callback, should you need it. This could be
|
|
||||||
* used for example to automatically unobserve the observer when a certain
|
|
||||||
* condition is reached, but you can omit it if you don't need it.
|
|
||||||
*
|
|
||||||
* The callback will generally follow a pattern along the lines of:
|
|
||||||
* ```js
|
|
||||||
* function(entries, observer) {
|
|
||||||
* for (let entry of entries) {
|
|
||||||
* // Do something to each entry
|
|
||||||
* // and possibly something to the observer itself
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* The following snippet is taken from the
|
|
||||||
* [resize-observer-text.html](https://mdn.github.io/dom-examples/resize-observer/resize-observer-text.html)
|
|
||||||
* ([see source](https://github.com/mdn/dom-examples/blob/master/resize-observer/resize-observer-text.html))
|
|
||||||
* example:
|
|
||||||
* @example
|
|
||||||
* const resizeObserver = new ResizeObserver(entries => {
|
|
||||||
* for (let entry of entries) {
|
|
||||||
* if(entry.contentBoxSize) {
|
|
||||||
* h1Elem.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize/200) + 'rem';
|
|
||||||
* pElem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize/600) + 'rem';
|
|
||||||
* } else {
|
|
||||||
* h1Elem.style.fontSize = Math.max(1.5, entry.contentRect.width/200) + 'rem';
|
|
||||||
* pElem.style.fontSize = Math.max(1, entry.contentRect.width/600) + 'rem';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* resizeObserver.observe(divElem);
|
|
||||||
*/
|
|
||||||
constructor(callback: ResizeObserverCallback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The **disconnect()** method of the
|
|
||||||
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
|
|
||||||
* interface unobserves all observed
|
|
||||||
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
|
||||||
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
|
|
||||||
* targets.
|
|
||||||
*/
|
|
||||||
disconnect: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The `observe()` method of the
|
|
||||||
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
|
|
||||||
* interface starts observing the specified
|
|
||||||
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
|
||||||
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* resizeObserver.observe(target, options);
|
|
||||||
*
|
|
||||||
* @param target
|
|
||||||
* A reference to an
|
|
||||||
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
|
||||||
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
|
|
||||||
* to be observed.
|
|
||||||
*
|
|
||||||
* @param options
|
|
||||||
* An options object allowing you to set options for the observation.
|
|
||||||
* Currently this only has one possible option that can be set.
|
|
||||||
*/
|
|
||||||
observe: (target: Element, options?: ResizeObserverObserveOptions) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The **unobserve()** method of the
|
|
||||||
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
|
|
||||||
* interface ends the observing of a specified
|
|
||||||
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
|
||||||
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
|
|
||||||
*/
|
|
||||||
unobserve: (target: Element) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ResizeObserverObserveOptions {
|
|
||||||
/**
|
|
||||||
* Sets which box model the observer will observe changes to. Possible values
|
|
||||||
* are `content-box` (the default), and `border-box`.
|
|
||||||
*
|
|
||||||
* @default "content-box"
|
|
||||||
*/
|
|
||||||
box?: "content-box" | "border-box";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The function called whenever an observed resize occurs. The function is
|
|
||||||
* called with two parameters:
|
|
||||||
*
|
|
||||||
* @param entries
|
|
||||||
* An array of
|
|
||||||
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
|
|
||||||
* objects that can be used to access the new dimensions of the element after
|
|
||||||
* each change.
|
|
||||||
*
|
|
||||||
* @param observer
|
|
||||||
* A reference to the `ResizeObserver` itself, so it will definitely be
|
|
||||||
* accessible from inside the callback, should you need it. This could be used
|
|
||||||
* for example to automatically unobserve the observer when a certain condition
|
|
||||||
* is reached, but you can omit it if you don't need it.
|
|
||||||
*
|
|
||||||
* The callback will generally follow a pattern along the lines of:
|
|
||||||
* @example
|
|
||||||
* function(entries, observer) {
|
|
||||||
* for (let entry of entries) {
|
|
||||||
* // Do something to each entry
|
|
||||||
* // and possibly something to the observer itself
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const resizeObserver = new ResizeObserver(entries => {
|
|
||||||
* for (let entry of entries) {
|
|
||||||
* if(entry.contentBoxSize) {
|
|
||||||
* h1Elem.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize/200) + 'rem';
|
|
||||||
* pElem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize/600) + 'rem';
|
|
||||||
* } else {
|
|
||||||
* h1Elem.style.fontSize = Math.max(1.5, entry.contentRect.width/200) + 'rem';
|
|
||||||
* pElem.style.fontSize = Math.max(1, entry.contentRect.width/600) + 'rem';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* resizeObserver.observe(divElem);
|
|
||||||
*/
|
|
||||||
type ResizeObserverCallback = (
|
|
||||||
entries: ResizeObserverEntry[],
|
|
||||||
observer: ResizeObserver,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The **ResizeObserverEntry** interface represents the object passed to the
|
|
||||||
* [ResizeObserver()](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver)
|
|
||||||
* constructor's callback function, which allows you to access the new
|
|
||||||
* dimensions of the
|
|
||||||
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
|
||||||
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
|
|
||||||
* being observed.
|
|
||||||
*/
|
|
||||||
interface ResizeObserverEntry {
|
|
||||||
/**
|
|
||||||
* An object containing the new border box size of the observed element when
|
|
||||||
* the callback is run.
|
|
||||||
*/
|
|
||||||
readonly borderBoxSize: ResizeObserverEntryBoxSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object containing the new content box size of the observed element when
|
|
||||||
* the callback is run.
|
|
||||||
*/
|
|
||||||
readonly contentBoxSize: ResizeObserverEntryBoxSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [DOMRectReadOnly](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly)
|
|
||||||
* object containing the new size of the observed element when the callback is
|
|
||||||
* run. Note that this is better supported than the above two properties, but
|
|
||||||
* it is left over from an earlier implementation of the Resize Observer API,
|
|
||||||
* is still included in the spec for web compat reasons, and may be deprecated
|
|
||||||
* in future versions.
|
|
||||||
*/
|
|
||||||
// node_modules/typescript/lib/lib.dom.d.ts
|
|
||||||
readonly contentRect: DOMRectReadOnly;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reference to the
|
|
||||||
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
|
||||||
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
|
|
||||||
* being observed.
|
|
||||||
*/
|
|
||||||
readonly target: Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The **borderBoxSize** read-only property of the
|
|
||||||
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
|
|
||||||
* interface returns an object containing the new border box size of the
|
|
||||||
* observed element when the callback is run.
|
|
||||||
*/
|
|
||||||
interface ResizeObserverEntryBoxSize {
|
|
||||||
/**
|
|
||||||
* The length of the observed element's border box in the block dimension. For
|
|
||||||
* boxes with a horizontal
|
|
||||||
* [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode),
|
|
||||||
* this is the vertical dimension, or height; if the writing-mode is vertical,
|
|
||||||
* this is the horizontal dimension, or width.
|
|
||||||
*/
|
|
||||||
blockSize: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the observed element's border box in the inline dimension.
|
|
||||||
* For boxes with a horizontal
|
|
||||||
* [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode),
|
|
||||||
* this is the horizontal dimension, or width; if the writing-mode is
|
|
||||||
* vertical, this is the vertical dimension, or height.
|
|
||||||
*/
|
|
||||||
inlineSize: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Window {
|
|
||||||
ResizeObserver: typeof ResizeObserver;
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
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,20 +1,13 @@
|
||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
computed,
|
computed,
|
||||||
watch,
|
|
||||||
PropType,
|
PropType,
|
||||||
ref,
|
ref,
|
||||||
Ref,
|
Ref,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { BlockData } from '../types';
|
import { BlockData } from '../types';
|
||||||
import { SbMode } from '../mode';
|
|
||||||
import { useResizeObserver, SymBlockDimensions } from '../use-resize-observer';
|
|
||||||
import { useActivation } from '../use-activation';
|
|
||||||
import { useBlockTree } from '../use-block-tree';
|
|
||||||
import { useDynamicBlocks } from '../use-dynamic-blocks';
|
import { useDynamicBlocks } from '../use-dynamic-blocks';
|
||||||
|
|
||||||
import SbMissingBlock from './MissingBlock';
|
|
||||||
|
|
||||||
import './Block.scss';
|
import './Block.scss';
|
||||||
|
|
||||||
export const SbBlock = defineComponent({
|
export const SbBlock = defineComponent({
|
||||||
|
@ -25,88 +18,24 @@ export const SbBlock = defineComponent({
|
||||||
type: (null as unknown) as PropType<BlockData<any>>,
|
type: (null as unknown) as PropType<BlockData<any>>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
sortable: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
onUpdate: { type: Function, default: () => {} },
|
|
||||||
onPrependBlock: { type: Function, default: () => {} },
|
|
||||||
onAppendBlock: { type: Function, default: () => {} },
|
|
||||||
onRemoveSelf: { type: Function, default: () => {} },
|
|
||||||
onActivatePrevious: { type: Function, default: () => {} },
|
|
||||||
onActivateNext: { type: Function, default: () => {} },
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
const el: Ref<null|HTMLElement> = ref(null);
|
const el: Ref<null|HTMLElement> = ref(null);
|
||||||
const { mode, getBlock } = useDynamicBlocks();
|
const { getBlock } = useDynamicBlocks();
|
||||||
const {
|
const classes = computed(() => ({ 'sb-block': true }));
|
||||||
isActive,
|
|
||||||
activate,
|
|
||||||
} = useActivation(props.block.id);
|
|
||||||
const classes = computed(() => ({
|
|
||||||
'sb-block': true,
|
|
||||||
'sb-block_active': isActive.value,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const { triggerSizeCalculation } = useResizeObserver(el, SymBlockDimensions);
|
|
||||||
watch(() => props.block.data, triggerSizeCalculation);
|
|
||||||
|
|
||||||
const { register } = useBlockTree();
|
|
||||||
register(props.block);
|
|
||||||
watch(props.block, () => { register(props.block); });
|
|
||||||
|
|
||||||
const onChildUpdate = (updated: {[key: string]: any}) => {
|
|
||||||
props.onUpdate({
|
|
||||||
...props.block,
|
|
||||||
data: {
|
|
||||||
...props.block.data,
|
|
||||||
...updated,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const BlockComponent = getBlock(props.block.name)?.[mode.value] as any;
|
const BlockComponent = getBlock(props.block.name)?.component as any;
|
||||||
|
|
||||||
if (!BlockComponent) {
|
|
||||||
const MissingBlock = SbMissingBlock[mode.value];
|
|
||||||
return <MissingBlock
|
|
||||||
name={props.block.name}
|
|
||||||
blockId={props.block.id}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode.value === SbMode.Display) {
|
|
||||||
return <BlockComponent
|
|
||||||
data={props.block.data}
|
|
||||||
blockId={props.block.id}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div
|
return <div
|
||||||
ref={el}
|
ref={el}
|
||||||
class={classes.value}
|
class={classes.value}
|
||||||
>
|
>
|
||||||
<div class="sb-block__edit-cover"></div>
|
|
||||||
{context.slots['context-toolbar'] ? context.slots['context-toolbar']() : null}
|
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
data={props.block.data}
|
data={props.block.data}
|
||||||
blockId={props.block.id}
|
blockId={props.block.id}
|
||||||
onUpdate={onChildUpdate}
|
{...context.attrs}
|
||||||
onPrependBlock={props.onPrependBlock}
|
|
||||||
onAppendBlock={props.onAppendBlock}
|
|
||||||
onRemoveSelf={props.onRemoveSelf}
|
|
||||||
onActivatePrevious={props.onActivatePrevious}
|
|
||||||
onActivateNext={props.onActivateNext}
|
|
||||||
|
|
||||||
{...{
|
|
||||||
onClick: ($event: MouseEvent) => {
|
|
||||||
$event.stopPropagation();
|
|
||||||
activate();
|
|
||||||
},
|
|
||||||
...context.attrs,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
.sb-block-ordering {
|
|
||||||
display: flex;
|
|
||||||
position: absolute;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
import { debounce } from 'lodash-es';
|
|
||||||
import {
|
|
||||||
watch,
|
|
||||||
reactive,
|
|
||||||
computed,
|
|
||||||
defineComponent,
|
|
||||||
} from 'vue';
|
|
||||||
import { useBlockSizing } from '../use-resize-observer';
|
|
||||||
|
|
||||||
import { SbButton } from './Button';
|
|
||||||
|
|
||||||
import './BlockOrdering.scss';
|
|
||||||
|
|
||||||
export const SbBlockOrdering = defineComponent({
|
|
||||||
name: 'sb-block-ordering',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
orientation: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
onRemove: { type: Function, default: () => {} },
|
|
||||||
onMoveBackward: { type: Function, default: () => {} },
|
|
||||||
onMoveForward: { type: Function, default: () => {} },
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const styles = reactive({
|
|
||||||
top: '',
|
|
||||||
right: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const classes = computed(() => ({
|
|
||||||
'sb-block-ordering': true,
|
|
||||||
[`sb-block-ordering_${props.orientation}`]: !!props.orientation,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const { editorDimensions, blockDimensions } = useBlockSizing();
|
|
||||||
const resetStyles = debounce(() => {
|
|
||||||
if (!editorDimensions.value || !blockDimensions.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const right = editorDimensions.value.width - blockDimensions.value.left;
|
|
||||||
styles.top = `${blockDimensions.value.top}px`;
|
|
||||||
styles.right = `${right}px`;
|
|
||||||
});
|
|
||||||
watch(editorDimensions, resetStyles);
|
|
||||||
watch(blockDimensions, resetStyles);
|
|
||||||
watch(() => props.orientation, resetStyles);
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div
|
|
||||||
class={classes.value}
|
|
||||||
style={styles}
|
|
||||||
onClick={($event: MouseEvent) => $event.stopPropagation()}
|
|
||||||
>
|
|
||||||
<SbButton {...{onClick: props.onMoveBackward}}>{props.orientation === 'vertical' ? '↑' : '←'}</SbButton>
|
|
||||||
<SbButton {...{onClick: props.onRemove}}>x</SbButton>
|
|
||||||
<SbButton {...{onClick: props.onMoveForward}}>{props.orientation === 'vertical' ? '↓' : '→'}</SbButton>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,11 +0,0 @@
|
||||||
.sb-block-picker {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&__add-button {
|
|
||||||
padding: 24px 32px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
import {
|
|
||||||
computed,
|
|
||||||
ref,
|
|
||||||
defineComponent,
|
|
||||||
} from 'vue';
|
|
||||||
import { useDynamicBlocks } from '../use-dynamic-blocks';
|
|
||||||
import { BlockDefinition } from '../types';
|
|
||||||
|
|
||||||
import { SbButton } from './Button';
|
|
||||||
import { SbModal } from './Modal';
|
|
||||||
|
|
||||||
import './BlockPicker.scss';
|
|
||||||
|
|
||||||
export const SbBlockPicker = defineComponent({
|
|
||||||
name: 'sb-block-picker',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
onPickedBlock: { type: Function, default: () => {} },
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const open = ref(false);
|
|
||||||
const { customBlocks } = useDynamicBlocks();
|
|
||||||
|
|
||||||
const blockList = computed(() => Object.keys(customBlocks).map((key) => customBlocks[key]));
|
|
||||||
|
|
||||||
const selectBlock = (block: BlockDefinition<any>) => () => {
|
|
||||||
open.value = false;
|
|
||||||
props.onPickedBlock({
|
|
||||||
name: block.name,
|
|
||||||
id: `${+(new Date())}`,
|
|
||||||
data: block.getDefaultData(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div class="sb-block-picker">
|
|
||||||
<SbButton
|
|
||||||
class="sb-block-picker__add-button"
|
|
||||||
{...{
|
|
||||||
type: 'button',
|
|
||||||
onClick: ($event: MouseEvent) => {
|
|
||||||
open.value = true;
|
|
||||||
$event.stopPropagation();
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>+</SbButton>
|
|
||||||
<SbModal
|
|
||||||
open={open.value}
|
|
||||||
onClose={() => {
|
|
||||||
open.value = false;
|
|
||||||
}}
|
|
||||||
{...{ onClick: ($event: MouseEvent) => $event.stopPropagation() }}
|
|
||||||
>
|
|
||||||
{...blockList.value.map((block: BlockDefinition<any>) => (
|
|
||||||
<SbButton
|
|
||||||
{...{
|
|
||||||
type: 'button',
|
|
||||||
onClick: () => selectBlock(block),
|
|
||||||
}}
|
|
||||||
>{block.name}</SbButton>
|
|
||||||
))}
|
|
||||||
</SbModal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,10 +0,0 @@
|
||||||
.sb-block-placeholder {
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
overflow: visible;
|
|
||||||
|
|
||||||
&__add {
|
|
||||||
background-color: var(--grey-1);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { BlockData } from '../types';
|
|
||||||
|
|
||||||
import { SbBlockPicker } from './BlockPicker';
|
|
||||||
|
|
||||||
import './BlockPlaceholder.scss';
|
|
||||||
|
|
||||||
export const SbBlockPlaceholder = defineComponent({
|
|
||||||
name: 'sb-block-placeholder',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
onInsertBlock: { type: Function, default: () => {} },
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
return () => (
|
|
||||||
<div class="sb-block-placeholder">
|
|
||||||
<SbBlockPicker
|
|
||||||
onPickedBlock={(block: BlockData<any>) => props.onInsertBlock(block)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import './BlockToolbar.scss';
|
|
||||||
|
|
||||||
export const SbBlockToolbar = defineComponent({
|
|
||||||
name: 'sb-block-toolbar',
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
return () => (
|
|
||||||
<div class="sb-block-toolbar"></div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,10 +0,0 @@
|
||||||
.sb-button {
|
|
||||||
border: 0;
|
|
||||||
padding: 8px 12px;
|
|
||||||
background-color: var(--grey-0);
|
|
||||||
border: 1px solid var(--grey-2);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border: 1px solid var(--interact);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import './Button.scss';
|
|
||||||
|
|
||||||
export const SbButton = defineComponent({
|
|
||||||
name: 'sb-button',
|
|
||||||
|
|
||||||
inheritAttrs: false,
|
|
||||||
|
|
||||||
setup(_, context) {
|
|
||||||
return () => (
|
|
||||||
<button
|
|
||||||
{...{
|
|
||||||
...context.attrs,
|
|
||||||
class: (context.attrs.class || '') + ' sb-button',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{context.slots.default?.()}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,22 +0,0 @@
|
||||||
.sb-context {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-context-menu {
|
|
||||||
display: none;
|
|
||||||
flex-direction: column;
|
|
||||||
background: var(--grey-0);
|
|
||||||
border: 1px solid var(--grey-3);
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
margin: 0;
|
|
||||||
z-index: var(--z-context-menu);
|
|
||||||
|
|
||||||
max-height: 70vh;
|
|
||||||
max-width: 100vw;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
&[open] {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
import {
|
|
||||||
watch,
|
|
||||||
defineComponent,
|
|
||||||
ref,
|
|
||||||
} from 'vue';
|
|
||||||
import { SbButton } from './Button';
|
|
||||||
|
|
||||||
import './ContextMenu.scss';
|
|
||||||
|
|
||||||
export const SbContextMenu = defineComponent({
|
|
||||||
name: 'sb-context-menu',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
onClose: { type: Function, default: () => {} },
|
|
||||||
onOpen: { type: Function, default: () => {} },
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props, context) {
|
|
||||||
const opened = ref(false);
|
|
||||||
const open = () => { opened.value = true; };
|
|
||||||
const close = () => { opened.value = false; };
|
|
||||||
const closeOnEscape = ($event: KeyboardEvent) => {
|
|
||||||
if ($event.key === 'Escape') {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const toggle = () => { opened.value ? close() : open() };
|
|
||||||
|
|
||||||
watch(opened, (curr, prev) => {
|
|
||||||
if (curr === prev) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!curr) {
|
|
||||||
document.body.removeEventListener('click', close);
|
|
||||||
document.body.removeEventListener('keypress', closeOnEscape);
|
|
||||||
props.onClose();
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
document.body.addEventListener('click', close);
|
|
||||||
document.body.addEventListener('keypress', closeOnEscape);
|
|
||||||
props.onOpen();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div class="sb-context">
|
|
||||||
{
|
|
||||||
context.slots.context?.({
|
|
||||||
opened,
|
|
||||||
toggle,
|
|
||||||
close,
|
|
||||||
open,
|
|
||||||
}) || <SbButton {...{ onClick: toggle }}>Menu</SbButton>
|
|
||||||
}
|
|
||||||
<dialog
|
|
||||||
class="sb-context-menu"
|
|
||||||
open={opened.value ? true : undefined}
|
|
||||||
onClick={($event: Event) => {
|
|
||||||
// Make sure clicks inside do not autoclose this
|
|
||||||
$event.stopPropagation();
|
|
||||||
}}
|
|
||||||
{...{ onClose: close /* TODO: DialogHTMLAttributes needs an onClose handler type */ }}
|
|
||||||
>
|
|
||||||
{context.slots.default?.({
|
|
||||||
opened,
|
|
||||||
toggle,
|
|
||||||
close,
|
|
||||||
open,
|
|
||||||
}) || null}
|
|
||||||
</dialog>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -4,27 +4,14 @@ import {
|
||||||
shallowReactive,
|
shallowReactive,
|
||||||
ref,
|
ref,
|
||||||
PropType,
|
PropType,
|
||||||
Ref,
|
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
BlockData,
|
BlockData,
|
||||||
BlockDefinition,
|
BlockDefinition,
|
||||||
BlockLibrary,
|
BlockLibrary,
|
||||||
TreeNode,
|
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { model } from '../block-helpers';
|
|
||||||
import { Mode, SbMode } from '../mode';
|
|
||||||
import { SymBlockLibrary} from '../use-dynamic-blocks';
|
import { SymBlockLibrary} from '../use-dynamic-blocks';
|
||||||
import {
|
|
||||||
SymBlockTree,
|
|
||||||
SymBlockTreeRegister,
|
|
||||||
SymBlockTreeUnregister,
|
|
||||||
} from '../use-block-tree';
|
|
||||||
import { SymEditorDimensions, useResizeObserver } from '../use-resize-observer';
|
|
||||||
import { SymActiveBlock } from '../use-activation';
|
|
||||||
|
|
||||||
import { SbMainMenu } from './MainMenu';
|
|
||||||
import { SbBlockToolbar } from './BlockToolbar';
|
|
||||||
import { SbBlock } from './Block';
|
import { SbBlock } from './Block';
|
||||||
|
|
||||||
import './Main.scss';
|
import './Main.scss';
|
||||||
|
@ -32,7 +19,10 @@ import './Main.scss';
|
||||||
export const SbMain = defineComponent({
|
export const SbMain = defineComponent({
|
||||||
name: 'sb-main',
|
name: 'sb-main',
|
||||||
|
|
||||||
model,
|
model: {
|
||||||
|
prop: 'block',
|
||||||
|
event: 'update',
|
||||||
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
customBlocks: {
|
customBlocks: {
|
||||||
|
@ -44,30 +34,9 @@ export const SbMain = defineComponent({
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
onUpdate: { type: Function, default: () => {} },
|
onUpdate: { type: Function, default: () => {} },
|
||||||
mode: {
|
|
||||||
type: String as PropType<SbMode>,
|
|
||||||
validator(value: any) {
|
|
||||||
return Object.values(SbMode).includes(value);
|
|
||||||
},
|
|
||||||
default: SbMode.Edit,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: any) { // TODO: why does the typing of props not work here?
|
setup(props: any) { // TODO: why does the typing of props not work here?
|
||||||
const el: Ref<null|HTMLElement> = ref(null);
|
|
||||||
useResizeObserver(el, SymEditorDimensions);
|
|
||||||
|
|
||||||
const mode = ref(props.mode);
|
|
||||||
provide(Mode, mode);
|
|
||||||
|
|
||||||
const activeBlock = ref(null);
|
|
||||||
provide(SymActiveBlock, activeBlock);
|
|
||||||
|
|
||||||
const blockTree: Ref<TreeNode|null> = ref(null);
|
|
||||||
provide(SymBlockTree, blockTree);
|
|
||||||
provide(SymBlockTreeRegister, (block: TreeNode) => { blockTree.value = block; });
|
|
||||||
provide(SymBlockTreeUnregister, () => { blockTree.value = null; });
|
|
||||||
|
|
||||||
const blockLibrary: BlockLibrary = shallowReactive({
|
const blockLibrary: BlockLibrary = shallowReactive({
|
||||||
...props.customBlocks.reduce(
|
...props.customBlocks.reduce(
|
||||||
(blocks: BlockLibrary, block: BlockDefinition<any>) => ({ ...blocks, [block.name]: block }),
|
(blocks: BlockLibrary, block: BlockDefinition<any>) => ({ ...blocks, [block.name]: block }),
|
||||||
|
@ -78,18 +47,7 @@ export const SbMain = defineComponent({
|
||||||
provide(SymBlockLibrary, blockLibrary);
|
provide(SymBlockLibrary, blockLibrary);
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div class="sb-main">
|
||||||
class="sb-main"
|
|
||||||
ref={el}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
mode.value === SbMode.Edit
|
|
||||||
? <>
|
|
||||||
<SbMainMenu block={props.block} />
|
|
||||||
<SbBlockToolbar />
|
|
||||||
</>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
<SbBlock
|
<SbBlock
|
||||||
block={props.block}
|
block={props.block}
|
||||||
onUpdate={props.onUpdate}
|
onUpdate={props.onUpdate}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
.sb-main-menu {
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
PropType,
|
|
||||||
} from 'vue';
|
|
||||||
import { BlockData } from '../types';
|
|
||||||
import { SbTreeBlockSelect } from './TreeBlockSelect';
|
|
||||||
|
|
||||||
import './MainMenu.scss';
|
|
||||||
|
|
||||||
export const SbMainMenu = defineComponent({
|
|
||||||
name: 'sb-main-menu',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
block: {
|
|
||||||
type: (null as unknown) as PropType<BlockData<any>>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
return () => (
|
|
||||||
<div class="sb-main-menu">
|
|
||||||
<SbTreeBlockSelect />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,31 +0,0 @@
|
||||||
import { defineComponent, PropType } from 'vue';
|
|
||||||
import {
|
|
||||||
model,
|
|
||||||
blockProps,
|
|
||||||
} from '../../block-helpers';
|
|
||||||
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'sb-missing-block',
|
|
||||||
|
|
||||||
model,
|
|
||||||
|
|
||||||
props: {
|
|
||||||
...blockProps,
|
|
||||||
name: String,
|
|
||||||
data: {
|
|
||||||
type: (null as unknown) as PropType<any>,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
eventUpdate: { type: Function, default: () => {} },
|
|
||||||
eventAppendBlock: { type: Function, default: () => {} },
|
|
||||||
eventRemoveBlock: { type: Function, default: () => {} },
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
return () => (
|
|
||||||
<div class="sb-missing-block">Missing block: {props.name}</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { defineAsyncComponent } from 'vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'sb-missing-block',
|
|
||||||
edit: defineAsyncComponent(() => import('./display')),
|
|
||||||
display: defineAsyncComponent(() => import('./display')),
|
|
||||||
};
|
|
|
@ -1,3 +0,0 @@
|
||||||
.sb-missing-block {
|
|
||||||
flex-basis: 100%;
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
.sb-modal {
|
|
||||||
&__overlay {
|
|
||||||
background-color: var(--grey-3-t);
|
|
||||||
position: fixed;
|
|
||||||
z-index: 10;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 10vh 10vw;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
width: 900px;
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
max-height: 100%;
|
|
||||||
background-color: var(--grey-0);
|
|
||||||
padding: 24px 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_open #{&}__overlay {
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
computed,
|
|
||||||
} from 'vue';
|
|
||||||
|
|
||||||
import './Modal.scss';
|
|
||||||
|
|
||||||
export const SbModal = defineComponent({
|
|
||||||
name: 'sb-modal',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
open: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
onClose: { type: Function, default: () => {} },
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props, context) {
|
|
||||||
const classes = computed(() => ({
|
|
||||||
'sb-modal': true,
|
|
||||||
'sb-modal_open': props.open,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div class={classes.value}>
|
|
||||||
<div
|
|
||||||
class="sb-modal__overlay"
|
|
||||||
onClick={($event: MouseEvent) => {
|
|
||||||
$event.stopPropagation();
|
|
||||||
props.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="sb-modal__content">
|
|
||||||
{context.slots.default?.()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,25 +0,0 @@
|
||||||
.sb-select {
|
|
||||||
background-color: var(--grey-0);
|
|
||||||
border: 1px solid var(--grey-2);
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border: 1px solid var(--interact);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
content: '⯆';
|
|
||||||
top: 6px;
|
|
||||||
height: 100%;
|
|
||||||
right: 12px;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__input {
|
|
||||||
background: transparent;
|
|
||||||
appearance: none;
|
|
||||||
border: 0;
|
|
||||||
padding: 8px 32px 8px 12px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import './Select.scss';
|
|
||||||
|
|
||||||
export const SbSelect = defineComponent({
|
|
||||||
name: 'sb-select',
|
|
||||||
|
|
||||||
inheritAttrs: false,
|
|
||||||
|
|
||||||
setup(_, context) {
|
|
||||||
return () => (
|
|
||||||
<div class="sb-select">
|
|
||||||
<select
|
|
||||||
class="sb-select__input"
|
|
||||||
{...context.attrs}
|
|
||||||
>
|
|
||||||
{context.slots.default?.()}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
.sb-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { debounce } from 'lodash-es';
|
|
||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
watch,
|
|
||||||
reactive,
|
|
||||||
} from 'vue';
|
|
||||||
import { useBlockSizing } from '../use-resize-observer';
|
|
||||||
|
|
||||||
import './Toolbar.scss';
|
|
||||||
|
|
||||||
export const SbToolbar = defineComponent({
|
|
||||||
name: 'sb-toolbar',
|
|
||||||
|
|
||||||
setup(_, context) {
|
|
||||||
const styles = reactive({
|
|
||||||
bottom: '',
|
|
||||||
left: '',
|
|
||||||
maxWidth: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const { editorDimensions, blockDimensions } = useBlockSizing();
|
|
||||||
const resetStyles = debounce(() => {
|
|
||||||
if (!editorDimensions.value || !blockDimensions.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bottom = editorDimensions.value.height - blockDimensions.value.top;
|
|
||||||
styles.bottom = `${bottom}px`;
|
|
||||||
styles.left = `${blockDimensions.value.left}px`;
|
|
||||||
styles.maxWidth = `${blockDimensions.value.width}px`;
|
|
||||||
});
|
|
||||||
watch(editorDimensions, resetStyles);
|
|
||||||
watch(blockDimensions, resetStyles);
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div
|
|
||||||
class="sb-toolbar"
|
|
||||||
style={styles}
|
|
||||||
onClick={($event: MouseEvent) => $event.stopPropagation()}
|
|
||||||
>
|
|
||||||
{context.slots?.default?.()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,38 +0,0 @@
|
||||||
.sb-tree-block-select {
|
|
||||||
&__list {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
&_base {
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__node {
|
|
||||||
}
|
|
||||||
|
|
||||||
&__block {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 1rem;
|
|
||||||
|
|
||||||
&-name {
|
|
||||||
display: block;
|
|
||||||
background: transparent;
|
|
||||||
border: 0;
|
|
||||||
font: inherit;
|
|
||||||
color: inherit;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&_active {
|
|
||||||
& > .sb-tree-block-select__block-name {
|
|
||||||
outline: 1px solid var(--interact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { TreeNode } from '../types';
|
|
||||||
import { useBlockTree } from '../use-block-tree';
|
|
||||||
import { useActivation } from '../use-activation';
|
|
||||||
|
|
||||||
import { SbContextMenu } from './ContextMenu';
|
|
||||||
import { SbButton } from './Button';
|
|
||||||
|
|
||||||
import './TreeBlockSelect.scss';
|
|
||||||
|
|
||||||
export const SbTreeBlockSelect = defineComponent({
|
|
||||||
name: 'sb-main-menu',
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
const { blockTree } = useBlockTree();
|
|
||||||
const {
|
|
||||||
activate,
|
|
||||||
activeBlockId,
|
|
||||||
} = useActivation();
|
|
||||||
|
|
||||||
const treeToHtml = (tree: TreeNode, close: Function) => <li
|
|
||||||
class={{
|
|
||||||
'sb-tree-block-select__block': true,
|
|
||||||
'sb-tree-block-select__block_active': activeBlockId.value === tree.id,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="sb-tree-block-select__block-name"
|
|
||||||
onClick={() => {
|
|
||||||
activate(tree.id);
|
|
||||||
close();
|
|
||||||
}}
|
|
||||||
onMouseenter={() => activate(tree.id)}
|
|
||||||
>{tree.name}</button>
|
|
||||||
{tree.children?.length
|
|
||||||
? <ul class="sb-tree-block-select__list">
|
|
||||||
{tree.children?.map((child: TreeNode) => treeToHtml(child, close))}
|
|
||||||
</ul>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
</li>;
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
blockTree.value
|
|
||||||
? <SbContextMenu
|
|
||||||
class="sb-tree-block-select"
|
|
||||||
v-slots={{
|
|
||||||
context: ({ toggle }: { toggle: Function }) => <SbButton {...{ onClick: toggle }}>Tree</SbButton>,
|
|
||||||
default: ({ close }: { close: Function }) => <ul
|
|
||||||
class="sb-tree-block-select__list sb-tree-block-select__list_base"
|
|
||||||
>{treeToHtml(blockTree.value as TreeNode, close)}</ul>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
: ''
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
export default {
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,19 +1,6 @@
|
||||||
export * from './mode';
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
export * from './block-helpers';
|
|
||||||
|
|
||||||
export * from './use-activation';
|
|
||||||
export * from './use-dynamic-blocks';
|
export * from './use-dynamic-blocks';
|
||||||
export * from './use-resize-observer';
|
|
||||||
|
|
||||||
export * from './directives/activation-cover.js';
|
|
||||||
|
|
||||||
export * from './components/Main';
|
export * from './components/Main';
|
||||||
export * from './components/Block';
|
export * from './components/Block';
|
||||||
export * from './components/BlockPicker';
|
|
||||||
export * from './components/BlockOrdering';
|
|
||||||
export * from './components/BlockPlaceholder';
|
|
||||||
export * from './components/Toolbar';
|
|
||||||
export * from './components/Button';
|
|
||||||
export * from './components/Select';
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
export enum SbMode {
|
|
||||||
Edit = 'edit',
|
|
||||||
Display = 'display',
|
|
||||||
}
|
|
||||||
export const Mode = Symbol('Schlechtenburg mode');
|
|
|
@ -28,8 +28,7 @@ export interface BlockDefinition<T> {
|
||||||
name: string;
|
name: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
getDefaultData: T;
|
getDefaultData: T;
|
||||||
edit: Component<BlockProps<T>>;
|
component: Component<BlockProps<T>>;
|
||||||
display: Component<BlockProps<T>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlockLibrary {
|
export interface BlockLibrary {
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import {
|
|
||||||
Ref,
|
|
||||||
ref,
|
|
||||||
inject,
|
|
||||||
computed,
|
|
||||||
} from 'vue';
|
|
||||||
|
|
||||||
export const SymActiveBlock = Symbol('Schlechtenburg active block');
|
|
||||||
export function useActivation(currentBlockId: string|null = null) {
|
|
||||||
const activeBlockId: Ref<string|null> = inject(SymActiveBlock, ref(null));
|
|
||||||
const isActive = computed(() => activeBlockId.value === currentBlockId);
|
|
||||||
const activate = (id?: string|null) => {
|
|
||||||
activeBlockId.value = id !== undefined ? id : currentBlockId;
|
|
||||||
};
|
|
||||||
const requestActivation = () => {
|
|
||||||
if (activeBlockId.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
activate();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
activeBlockId,
|
|
||||||
isActive,
|
|
||||||
activate,
|
|
||||||
requestActivation,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
import {
|
|
||||||
ref,
|
|
||||||
Ref,
|
|
||||||
reactive,
|
|
||||||
inject,
|
|
||||||
provide,
|
|
||||||
onUnmounted,
|
|
||||||
} from 'vue';
|
|
||||||
import {
|
|
||||||
TreeNode,
|
|
||||||
BlockData,
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
export const SymBlockTree= Symbol('Schlechtenburg block tree');
|
|
||||||
export const SymBlockTreeRegister = Symbol('Schlechtenburg block tree register');
|
|
||||||
export const SymBlockTreeUnregister = Symbol('Schlechtenburg block tree unregister');
|
|
||||||
|
|
||||||
export function useBlockTree() {
|
|
||||||
const blockTree: Ref<TreeNode|null> = inject(SymBlockTree, ref(null));
|
|
||||||
const registerWithParent = inject(SymBlockTreeRegister, (_: TreeNode) => {});
|
|
||||||
const unregisterWithParent = inject(SymBlockTreeUnregister, (_: TreeNode) => {});
|
|
||||||
|
|
||||||
const self: TreeNode = reactive({
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
icon: '',
|
|
||||||
children: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Provide a registration function to child blocks
|
|
||||||
provide(SymBlockTreeRegister, (block: TreeNode) => {
|
|
||||||
if (self.children.find((child: TreeNode) => child.id === block.id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.children = [
|
|
||||||
...self.children,
|
|
||||||
block,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
// Provide an unregistration function to child blocks
|
|
||||||
provide(SymBlockTreeUnregister, ({ id }: TreeNode) => {
|
|
||||||
self.children = self.children.filter((child: TreeNode) => child.id !== id);
|
|
||||||
});
|
|
||||||
|
|
||||||
const register = (block: BlockData<any>) => {
|
|
||||||
if (!block.id) {
|
|
||||||
throw new Error(`Cannot register a block without an id: ${JSON.stringify(block)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.id = block.id;
|
|
||||||
self.name = block.name;
|
|
||||||
|
|
||||||
// Register ourselves at the parent block
|
|
||||||
registerWithParent(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unregister from parent when we get destroyed
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (self.id) {
|
|
||||||
unregisterWithParent(self);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
blockTree,
|
|
||||||
register,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,19 +1,15 @@
|
||||||
import {
|
import {
|
||||||
ref,
|
|
||||||
inject,
|
inject,
|
||||||
reactive,
|
reactive,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { BlockLibrary } from './types';
|
import { BlockLibrary } from './types';
|
||||||
import { Mode, SbMode } from './mode';
|
|
||||||
|
|
||||||
export const SymBlockLibrary = 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 customBlocks: BlockLibrary = inject(SymBlockLibrary, reactive({}));
|
const customBlocks: BlockLibrary = inject(SymBlockLibrary, reactive({}));
|
||||||
const getBlock = (name: string) => customBlocks[name];
|
const getBlock = (name: string) => customBlocks[name];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mode,
|
|
||||||
customBlocks,
|
customBlocks,
|
||||||
getBlock,
|
getBlock,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
/// <reference types="resize-observer-browser" />
|
|
||||||
|
|
||||||
import {
|
|
||||||
Ref,
|
|
||||||
ref,
|
|
||||||
inject,
|
|
||||||
watch,
|
|
||||||
provide,
|
|
||||||
} from 'vue';
|
|
||||||
|
|
||||||
interface BlockRect {
|
|
||||||
height: number;
|
|
||||||
width: number;
|
|
||||||
left: number;
|
|
||||||
top: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SymBlockDimensions = Symbol('Schlechtenburg block dimensions');
|
|
||||||
export const SymEditorDimensions = Symbol('Schlechtenburg editor dimensions');
|
|
||||||
export function useResizeObserver(el: Ref<null|HTMLElement>, symbol: symbol) {
|
|
||||||
const dimensions: Ref<null|BlockRect> = ref(null);
|
|
||||||
provide(symbol, dimensions);
|
|
||||||
const triggerSizeCalculation = () => {
|
|
||||||
if (!el.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientRect = el.value.getBoundingClientRect();
|
|
||||||
dimensions.value = {
|
|
||||||
width: clientRect.width,
|
|
||||||
height: clientRect.height,
|
|
||||||
left: el.value.offsetLeft,
|
|
||||||
top: el.value.offsetTop,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(triggerSizeCalculation);
|
|
||||||
const mutationObserver = new MutationObserver(triggerSizeCalculation);
|
|
||||||
|
|
||||||
watch(el, () => {
|
|
||||||
if (!el.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resizeObserver.observe(el.value);
|
|
||||||
mutationObserver.observe(el.value, { attributes: true, childList: false, subtree: false });
|
|
||||||
});
|
|
||||||
|
|
||||||
return { triggerSizeCalculation, dimensions };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useBlockSizing() {
|
|
||||||
const editorDimensions: Ref<BlockRect|null> = inject(SymEditorDimensions, ref(null));
|
|
||||||
const blockDimensions: Ref<BlockRect|null> = inject(SymBlockDimensions, ref(null));
|
|
||||||
|
|
||||||
return { editorDimensions, blockDimensions };
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
# `@schlechtenburg/heading`
|
|
||||||
|
|
||||||
> TODO: description
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```
|
|
||||||
const heading = require('@schlechtenburg/heading');
|
|
||||||
|
|
||||||
// TODO: DEMONSTRATE API
|
|
||||||
```
|
|
|
@ -1,7 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const heading = require('..');
|
|
||||||
|
|
||||||
describe('@schlechtenburg/heading', () => {
|
|
||||||
it('needs tests');
|
|
||||||
});
|
|
|
@ -1 +0,0 @@
|
||||||
export default {};
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { defineAsyncComponent } from 'vue';
|
|
||||||
import { getDefaultData } from './util';
|
|
||||||
|
|
||||||
export * from './util';
|
|
||||||
export const name = 'sb-heading';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name,
|
|
||||||
getDefaultData,
|
|
||||||
edit: defineAsyncComponent(() => import('./edit')),
|
|
||||||
display: defineAsyncComponent(() => import('./edit')),
|
|
||||||
};
|
|
|
@ -1,2 +0,0 @@
|
||||||
export const a = 1;
|
|
||||||
export const getDefaultData = () => ({});
|
|
116
packages/heading/package-lock.json
generated
116
packages/heading/package-lock.json
generated
|
@ -1,116 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@schlechtenburg/heading",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"requires": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/helper-validator-identifier": {
|
|
||||||
"version": "7.12.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
|
|
||||||
"integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw=="
|
|
||||||
},
|
|
||||||
"@babel/parser": {
|
|
||||||
"version": "7.13.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.9.tgz",
|
|
||||||
"integrity": "sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw=="
|
|
||||||
},
|
|
||||||
"@babel/types": {
|
|
||||||
"version": "7.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz",
|
|
||||||
"integrity": "sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/helper-validator-identifier": "^7.12.11",
|
|
||||||
"lodash": "^4.17.19",
|
|
||||||
"to-fast-properties": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/compiler-core": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-JFohgBXoyUc3mdeI2WxlhjQZ5fakfemJkZHX8Gu/nFbEg3+lKVUZmNKWmmnp9aOzJQZKoj77LjmFxiP+P+7lMQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/parser": "^7.12.0",
|
|
||||||
"@babel/types": "^7.12.0",
|
|
||||||
"@vue/shared": "3.0.7",
|
|
||||||
"estree-walker": "^2.0.1",
|
|
||||||
"source-map": "^0.6.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/compiler-dom": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-VnIH9EbWQm/Tkcp+8dCaNVsVvhm/vxCrIKWRkXY9215hTqOqQOvejT8IMjd2kc++nIsYMsdQk6H9qqBvoLe/Cw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/compiler-core": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/reactivity": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-FotWcNNaKhqpFZrdgsUOZ1enlJ5lhTt01CNTtLSyK7jYFgZBTuw8vKsEutZKDYZ1XKotOfoeO8N3pZQqmM6Etw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/runtime-core": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-DBAZAwVvdmMXuyd6/9qqj/kYr/GaLTmn1L2/QLxLwP+UfhIboiTSBc/tUUb8MRk7Bb98GzNeAWkkT6AfooS3dQ==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/reactivity": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/runtime-dom": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-Oij4ruOtnpQpCj+/Q3JPzgpTJ1Q7+N67pA53A8KVITEtxfvKL46NN6dhAZ5NGqwX6RWZpYqWQNewITeF0pHr8g==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/runtime-core": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7",
|
|
||||||
"csstype": "^2.6.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/shared": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-dn5FyfSc4ky424jH4FntiHno7Ss5yLkqKNmM/NXwANRnlkmqu74pnGetexDFVG5phMk9/FhwovUZCWGxsotVKg=="
|
|
||||||
},
|
|
||||||
"csstype": {
|
|
||||||
"version": "2.6.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz",
|
|
||||||
"integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q=="
|
|
||||||
},
|
|
||||||
"estree-walker": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
|
||||||
},
|
|
||||||
"lodash": {
|
|
||||||
"version": "4.17.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
|
||||||
},
|
|
||||||
"source-map": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
|
||||||
},
|
|
||||||
"to-fast-properties": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
|
||||||
},
|
|
||||||
"vue": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-8h4TikD+JabbMK9aRlBO4laG0AtNHRPHynxYgWZ9sq1YUPfzynd9Jeeb27XNyZytC7aCQRX9xe1+TQJuc181Tw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/compiler-dom": "3.0.7",
|
|
||||||
"@vue/runtime-dom": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@schlechtenburg/heading",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"description": "> TODO: description",
|
|
||||||
"author": "Benjamin Bädorf <hello@benjaminbaedorf.eu>",
|
|
||||||
"homepage": "",
|
|
||||||
"license": "GPL-3.0-or-later",
|
|
||||||
"main": "lib/index.ts",
|
|
||||||
"directories": {
|
|
||||||
"lib": "lib",
|
|
||||||
"test": "__tests__"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@git.b12f.io:b12f/schlechtenburg.git"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@schlechtenburg/core": "^0.0.0",
|
|
||||||
"vue": "^3.0.7"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
# `@schlechtenburg/image`
|
|
||||||
|
|
||||||
> TODO: description
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```
|
|
||||||
const image = require('@schlechtenburg/image');
|
|
||||||
|
|
||||||
// TODO: DEMONSTRATE API
|
|
||||||
```
|
|
|
@ -1,7 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const image = require('..');
|
|
||||||
|
|
||||||
describe('@schlechtenburg/image', () => {
|
|
||||||
it('needs tests');
|
|
||||||
});
|
|
|
@ -1,35 +0,0 @@
|
||||||
import { defineComponent, PropType } from 'vue';
|
|
||||||
import {
|
|
||||||
model,
|
|
||||||
SbBlock,
|
|
||||||
} from '@schlechtenburg/core';
|
|
||||||
import {
|
|
||||||
getDefaultData,
|
|
||||||
ImageData,
|
|
||||||
} from './util';
|
|
||||||
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'sb-image-display',
|
|
||||||
|
|
||||||
model,
|
|
||||||
|
|
||||||
props: {
|
|
||||||
data: {
|
|
||||||
type: (null as unknown) as PropType<ImageData>,
|
|
||||||
default: getDefaultData,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
return () => <figure class="sb-image">
|
|
||||||
<img
|
|
||||||
class="sb-image__content"
|
|
||||||
src={props.data.src}
|
|
||||||
alt={props.data.alt}
|
|
||||||
/>
|
|
||||||
<SbBlock block={props.data.description} />
|
|
||||||
</figure>;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,115 +0,0 @@
|
||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
reactive,
|
|
||||||
ref,
|
|
||||||
Ref,
|
|
||||||
watch,
|
|
||||||
PropType,
|
|
||||||
} from 'vue';
|
|
||||||
import {
|
|
||||||
model,
|
|
||||||
SbToolbar,
|
|
||||||
SbButton,
|
|
||||||
SbBlock,
|
|
||||||
BlockData,
|
|
||||||
} from '@schlechtenburg/core';
|
|
||||||
import { ParagraphData } from '@schlechtenburg/paragraph';
|
|
||||||
import {
|
|
||||||
getDefaultData,
|
|
||||||
ImageData,
|
|
||||||
} from './util';
|
|
||||||
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'sb-image-edit',
|
|
||||||
|
|
||||||
model,
|
|
||||||
|
|
||||||
props: {
|
|
||||||
onUpdate: { type: Function, default: () => {} },
|
|
||||||
data: {
|
|
||||||
type: (null as unknown) as PropType<ImageData>,
|
|
||||||
default: getDefaultData,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const localData = reactive({
|
|
||||||
src: props.data.src,
|
|
||||||
alt: props.data.alt,
|
|
||||||
description: props.data.description,
|
|
||||||
});
|
|
||||||
|
|
||||||
const fileInput: Ref<null|HTMLInputElement> = ref(null);
|
|
||||||
|
|
||||||
watch(() => props.data, () => {
|
|
||||||
localData.src = props.data.src;
|
|
||||||
localData.alt = props.data.alt;
|
|
||||||
localData.description = props.data.description;
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectImage = () => {
|
|
||||||
if (fileInput.value) {
|
|
||||||
fileInput.value.click();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onImageSelect = () => {
|
|
||||||
if (fileInput.value && fileInput.value.files && fileInput.value.files.length) {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.addEventListener('load', () => {
|
|
||||||
const src = reader?.result?.toString();
|
|
||||||
if (!src) {
|
|
||||||
throw new Error('Couldn\'t load image src');
|
|
||||||
}
|
|
||||||
|
|
||||||
props.onUpdate({
|
|
||||||
src,
|
|
||||||
alt: props.data.alt,
|
|
||||||
description: props.data.description,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
reader.readAsDataURL(fileInput.value.files[0]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDescriptionUpdate = (description: BlockData<ParagraphData>) => {
|
|
||||||
props.onUpdate({
|
|
||||||
...props.data,
|
|
||||||
description,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<figure class="sb-image">
|
|
||||||
<SbToolbar>
|
|
||||||
{localData.src
|
|
||||||
? <SbButton {...{ onClick: selectImage }}>Select Image</SbButton>
|
|
||||||
: null}
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
ref={fileInput}
|
|
||||||
style="display: none;"
|
|
||||||
onInput={onImageSelect}
|
|
||||||
/>
|
|
||||||
</SbToolbar>
|
|
||||||
{localData.src
|
|
||||||
? <>
|
|
||||||
<img
|
|
||||||
src={localData.src}
|
|
||||||
alt={localData.alt}
|
|
||||||
class="sb-image__content"
|
|
||||||
/>
|
|
||||||
<SbBlock
|
|
||||||
block={localData.description}
|
|
||||||
onUpdate={(updated: BlockData<ParagraphData>) => onDescriptionUpdate(updated)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
: <SbButton {...{ onClick: selectImage }}>Select Image</SbButton>
|
|
||||||
}
|
|
||||||
</figure>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { defineAsyncComponent } from 'vue';
|
|
||||||
import { getDefaultData } from './util';
|
|
||||||
|
|
||||||
export * from './util';
|
|
||||||
export const name = 'sb-image';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name,
|
|
||||||
getDefaultData,
|
|
||||||
edit: defineAsyncComponent(() => import('./edit')),
|
|
||||||
display: defineAsyncComponent(() => import('./display')),
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
.sb-image {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
import {
|
|
||||||
BlockData,
|
|
||||||
generateBlockId,
|
|
||||||
} from '@schlechtenburg/core';
|
|
||||||
import {
|
|
||||||
name as paragraphName,
|
|
||||||
ParagraphData,
|
|
||||||
getDefaultData as getDefaultParagraphData
|
|
||||||
} from '@schlechtenburg/paragraph';
|
|
||||||
|
|
||||||
export interface ImageData {
|
|
||||||
src: string;
|
|
||||||
alt: string;
|
|
||||||
description: BlockData<ParagraphData>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getDefaultData: () => ImageData = () => ({
|
|
||||||
src: '',
|
|
||||||
alt: '',
|
|
||||||
description: {
|
|
||||||
id: generateBlockId(),
|
|
||||||
name: paragraphName,
|
|
||||||
data: getDefaultParagraphData(),
|
|
||||||
},
|
|
||||||
});
|
|
116
packages/image/package-lock.json
generated
116
packages/image/package-lock.json
generated
|
@ -1,116 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@schlechtenburg/image",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"requires": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/helper-validator-identifier": {
|
|
||||||
"version": "7.12.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
|
|
||||||
"integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw=="
|
|
||||||
},
|
|
||||||
"@babel/parser": {
|
|
||||||
"version": "7.13.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.9.tgz",
|
|
||||||
"integrity": "sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw=="
|
|
||||||
},
|
|
||||||
"@babel/types": {
|
|
||||||
"version": "7.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz",
|
|
||||||
"integrity": "sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/helper-validator-identifier": "^7.12.11",
|
|
||||||
"lodash": "^4.17.19",
|
|
||||||
"to-fast-properties": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/compiler-core": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-JFohgBXoyUc3mdeI2WxlhjQZ5fakfemJkZHX8Gu/nFbEg3+lKVUZmNKWmmnp9aOzJQZKoj77LjmFxiP+P+7lMQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/parser": "^7.12.0",
|
|
||||||
"@babel/types": "^7.12.0",
|
|
||||||
"@vue/shared": "3.0.7",
|
|
||||||
"estree-walker": "^2.0.1",
|
|
||||||
"source-map": "^0.6.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/compiler-dom": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-VnIH9EbWQm/Tkcp+8dCaNVsVvhm/vxCrIKWRkXY9215hTqOqQOvejT8IMjd2kc++nIsYMsdQk6H9qqBvoLe/Cw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/compiler-core": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/reactivity": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-FotWcNNaKhqpFZrdgsUOZ1enlJ5lhTt01CNTtLSyK7jYFgZBTuw8vKsEutZKDYZ1XKotOfoeO8N3pZQqmM6Etw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/runtime-core": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-DBAZAwVvdmMXuyd6/9qqj/kYr/GaLTmn1L2/QLxLwP+UfhIboiTSBc/tUUb8MRk7Bb98GzNeAWkkT6AfooS3dQ==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/reactivity": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/runtime-dom": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-Oij4ruOtnpQpCj+/Q3JPzgpTJ1Q7+N67pA53A8KVITEtxfvKL46NN6dhAZ5NGqwX6RWZpYqWQNewITeF0pHr8g==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/runtime-core": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7",
|
|
||||||
"csstype": "^2.6.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/shared": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-dn5FyfSc4ky424jH4FntiHno7Ss5yLkqKNmM/NXwANRnlkmqu74pnGetexDFVG5phMk9/FhwovUZCWGxsotVKg=="
|
|
||||||
},
|
|
||||||
"csstype": {
|
|
||||||
"version": "2.6.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz",
|
|
||||||
"integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q=="
|
|
||||||
},
|
|
||||||
"estree-walker": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
|
||||||
},
|
|
||||||
"lodash": {
|
|
||||||
"version": "4.17.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
|
||||||
},
|
|
||||||
"source-map": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
|
||||||
},
|
|
||||||
"to-fast-properties": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
|
||||||
},
|
|
||||||
"vue": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-8h4TikD+JabbMK9aRlBO4laG0AtNHRPHynxYgWZ9sq1YUPfzynd9Jeeb27XNyZytC7aCQRX9xe1+TQJuc181Tw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/compiler-dom": "3.0.7",
|
|
||||||
"@vue/runtime-dom": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@schlechtenburg/image",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"description": "> TODO: description",
|
|
||||||
"author": "Benjamin Bädorf <hello@benjaminbaedorf.eu>",
|
|
||||||
"homepage": "",
|
|
||||||
"license": "GPL-3.0-or-later",
|
|
||||||
"main": "lib/index.ts",
|
|
||||||
"directories": {
|
|
||||||
"lib": "lib",
|
|
||||||
"test": "__tests__"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@git.b12f.io:b12f/schlechtenburg.git"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@schlechtenburg/core": "^0.0.0",
|
|
||||||
"@schlechtenburg/paragraph": "^0.0.0",
|
|
||||||
"vue": "^3.0.7"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
# `@schlechtenburg/layout`
|
|
||||||
|
|
||||||
> TODO: description
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```
|
|
||||||
const layout = require('@schlechtenburg/layout');
|
|
||||||
|
|
||||||
// TODO: DEMONSTRATE API
|
|
||||||
```
|
|
|
@ -1,7 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const layout = require('..');
|
|
||||||
|
|
||||||
describe('@schlechtenburg/layout', () => {
|
|
||||||
it('needs tests');
|
|
||||||
});
|
|
|
@ -1,46 +0,0 @@
|
||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
computed,
|
|
||||||
PropType,
|
|
||||||
} from 'vue';
|
|
||||||
import {
|
|
||||||
model,
|
|
||||||
SbBlock,
|
|
||||||
} from '@schlechtenburg/core';
|
|
||||||
import {
|
|
||||||
LayoutData,
|
|
||||||
getDefaultData,
|
|
||||||
} from './util';
|
|
||||||
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'sb-layout-display',
|
|
||||||
|
|
||||||
model,
|
|
||||||
|
|
||||||
props: {
|
|
||||||
data: {
|
|
||||||
type: (null as unknown) as PropType<LayoutData>,
|
|
||||||
default: getDefaultData,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const classes = computed(() => ({
|
|
||||||
'sb-layout': true,
|
|
||||||
[`sb-layout_${props.data.orientation}`]: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div class={classes.value}>
|
|
||||||
{...props.data.children.map((child) => (
|
|
||||||
<SbBlock
|
|
||||||
key={child.id}
|
|
||||||
block={child}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,196 +0,0 @@
|
||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
reactive,
|
|
||||||
computed,
|
|
||||||
watch,
|
|
||||||
PropType,
|
|
||||||
} from 'vue';
|
|
||||||
import {
|
|
||||||
model,
|
|
||||||
BlockData,
|
|
||||||
useActivation,
|
|
||||||
|
|
||||||
SbBlock,
|
|
||||||
SbButton,
|
|
||||||
SbToolbar,
|
|
||||||
SbBlockPlaceholder,
|
|
||||||
SbBlockOrdering,
|
|
||||||
} from '@schlechtenburg/core';
|
|
||||||
|
|
||||||
import {
|
|
||||||
LayoutData,
|
|
||||||
getDefaultData,
|
|
||||||
} from './util';
|
|
||||||
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'sb-layout-edit',
|
|
||||||
|
|
||||||
model,
|
|
||||||
|
|
||||||
props: {
|
|
||||||
onUpdate: { type: Function, default: () => {} },
|
|
||||||
data: {
|
|
||||||
type: (null as unknown) as PropType<LayoutData>,
|
|
||||||
default: getDefaultData,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const { activate } = useActivation();
|
|
||||||
|
|
||||||
const localData: LayoutData = reactive({
|
|
||||||
orientation: props.data.orientation,
|
|
||||||
children: [...props.data.children],
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(() => props.data, () => {
|
|
||||||
localData.orientation = props.data.orientation;
|
|
||||||
localData.children = [...props.data.children];
|
|
||||||
});
|
|
||||||
|
|
||||||
const classes = computed(() => ({
|
|
||||||
'sb-layout': true,
|
|
||||||
[`sb-layout_${localData.orientation}`]: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const toggleOrientation = () => {
|
|
||||||
props.onUpdate({
|
|
||||||
orientation: localData.orientation === 'vertical' ? 'horizontal' : 'vertical',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChildUpdate = (child: BlockData<any>, updated: BlockData<any>) => {
|
|
||||||
const index = localData.children.indexOf(child);
|
|
||||||
if (index === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
props.onUpdate({
|
|
||||||
children: [
|
|
||||||
...localData.children.slice(0, index),
|
|
||||||
{
|
|
||||||
...child,
|
|
||||||
...updated,
|
|
||||||
},
|
|
||||||
...localData.children.slice(index + 1),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const appendBlock = (block: BlockData<any>) => {
|
|
||||||
localData.children = [
|
|
||||||
...localData.children,
|
|
||||||
block,
|
|
||||||
];
|
|
||||||
props.onUpdate({ children: [...localData.children] });
|
|
||||||
activate(block.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const insertBlock = (index: number, block: BlockData<any>) => {
|
|
||||||
localData.children = [
|
|
||||||
...localData.children.slice(0, index + 1),
|
|
||||||
block,
|
|
||||||
...localData.children.slice(index + 1),
|
|
||||||
];
|
|
||||||
props.onUpdate({ children: [...localData.children] });
|
|
||||||
activate(block.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeBlock = (index: number) => {
|
|
||||||
localData.children = [
|
|
||||||
...localData.children.slice(0, index),
|
|
||||||
...localData.children.slice(index + 1),
|
|
||||||
];
|
|
||||||
props.onUpdate({ children: [...localData.children] });
|
|
||||||
|
|
||||||
const newActiveIndex = Math.max(index - 1, 0);
|
|
||||||
activate(localData.children[newActiveIndex].id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const activateBlock = (index: number) => {
|
|
||||||
const safeIndex =
|
|
||||||
Math.max(
|
|
||||||
Math.min(
|
|
||||||
localData.children.length - 1,
|
|
||||||
index,
|
|
||||||
),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
activate(localData.children[safeIndex].id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const moveBackward = (index: number) => {
|
|
||||||
if (index === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const curr = localData.children[index];
|
|
||||||
const prev = localData.children[index - 1];
|
|
||||||
localData.children = [
|
|
||||||
...localData.children.slice(0, index - 1),
|
|
||||||
curr,
|
|
||||||
prev,
|
|
||||||
...localData.children.slice(index + 1),
|
|
||||||
];
|
|
||||||
|
|
||||||
props.onUpdate({ children: [...localData.children] });
|
|
||||||
};
|
|
||||||
|
|
||||||
const moveForward = (index: number) => {
|
|
||||||
if (index === localData.children.length - 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const curr = localData.children[index];
|
|
||||||
const next = localData.children[index + 1];
|
|
||||||
localData.children = [
|
|
||||||
...localData.children.slice(0, index),
|
|
||||||
next,
|
|
||||||
curr,
|
|
||||||
...localData.children.slice(index + 2),
|
|
||||||
];
|
|
||||||
|
|
||||||
props.onUpdate({ children: [...localData.children] });
|
|
||||||
};
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div class={classes.value}>
|
|
||||||
<SbToolbar>
|
|
||||||
<SbButton
|
|
||||||
{...{
|
|
||||||
type: 'button',
|
|
||||||
onClick: toggleOrientation,
|
|
||||||
}}
|
|
||||||
>{localData.orientation}</SbButton>
|
|
||||||
</SbToolbar>
|
|
||||||
|
|
||||||
{...localData.children.map((child, index) => (
|
|
||||||
<SbBlock
|
|
||||||
{...{ key: child.id }}
|
|
||||||
data-order={index}
|
|
||||||
block={child}
|
|
||||||
onUpdate={(updated: BlockData<any>) => onChildUpdate(child, updated)}
|
|
||||||
onRemoveSelf={() => removeBlock(index)}
|
|
||||||
onPrependBlock={(block: BlockData<any>) => insertBlock(index - 1, block)}
|
|
||||||
onAppendBlock={(block: BlockData<any>) => insertBlock(index, block)}
|
|
||||||
onActivatePrevious={() => activateBlock(index - 1,)}
|
|
||||||
onActivateNext={() => activateBlock(index + 1,)}
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
'context-toolbar': () =>
|
|
||||||
<SbBlockOrdering
|
|
||||||
onMoveBackward={() => moveBackward(index)}
|
|
||||||
onMoveForward={() => moveForward(index)}
|
|
||||||
onRemove={() => removeBlock(index)}
|
|
||||||
orientation={localData.orientation}
|
|
||||||
/>,
|
|
||||||
}}
|
|
||||||
</SbBlock>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<SbBlockPlaceholder onInsertBlock={appendBlock} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { defineAsyncComponent } from 'vue';
|
|
||||||
import { getDefaultData } from './util';
|
|
||||||
|
|
||||||
export * from './util';
|
|
||||||
export const name = 'sb-layout';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name,
|
|
||||||
getDefaultData,
|
|
||||||
edit: defineAsyncComponent(() => import('./edit')),
|
|
||||||
display: defineAsyncComponent(() => import('./display')),
|
|
||||||
};
|
|
|
@ -1,21 +0,0 @@
|
||||||
.sb-layout {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&_vertical {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_horizontal {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__item {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
> * {
|
|
||||||
flex-basis: auto;
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { BlockData } from '@schlechtenburg/core';
|
|
||||||
|
|
||||||
export interface LayoutData {
|
|
||||||
orientation: string;
|
|
||||||
children: BlockData<any>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getDefaultData: () => LayoutData = () => ({
|
|
||||||
orientation: 'vertical',
|
|
||||||
children: [],
|
|
||||||
});
|
|
116
packages/layout/package-lock.json
generated
116
packages/layout/package-lock.json
generated
|
@ -1,116 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@schlechtenburg/layout",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"requires": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/helper-validator-identifier": {
|
|
||||||
"version": "7.12.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
|
|
||||||
"integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw=="
|
|
||||||
},
|
|
||||||
"@babel/parser": {
|
|
||||||
"version": "7.13.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.9.tgz",
|
|
||||||
"integrity": "sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw=="
|
|
||||||
},
|
|
||||||
"@babel/types": {
|
|
||||||
"version": "7.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz",
|
|
||||||
"integrity": "sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/helper-validator-identifier": "^7.12.11",
|
|
||||||
"lodash": "^4.17.19",
|
|
||||||
"to-fast-properties": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/compiler-core": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-JFohgBXoyUc3mdeI2WxlhjQZ5fakfemJkZHX8Gu/nFbEg3+lKVUZmNKWmmnp9aOzJQZKoj77LjmFxiP+P+7lMQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/parser": "^7.12.0",
|
|
||||||
"@babel/types": "^7.12.0",
|
|
||||||
"@vue/shared": "3.0.7",
|
|
||||||
"estree-walker": "^2.0.1",
|
|
||||||
"source-map": "^0.6.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/compiler-dom": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-VnIH9EbWQm/Tkcp+8dCaNVsVvhm/vxCrIKWRkXY9215hTqOqQOvejT8IMjd2kc++nIsYMsdQk6H9qqBvoLe/Cw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/compiler-core": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/reactivity": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-FotWcNNaKhqpFZrdgsUOZ1enlJ5lhTt01CNTtLSyK7jYFgZBTuw8vKsEutZKDYZ1XKotOfoeO8N3pZQqmM6Etw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/runtime-core": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-DBAZAwVvdmMXuyd6/9qqj/kYr/GaLTmn1L2/QLxLwP+UfhIboiTSBc/tUUb8MRk7Bb98GzNeAWkkT6AfooS3dQ==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/reactivity": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/runtime-dom": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-Oij4ruOtnpQpCj+/Q3JPzgpTJ1Q7+N67pA53A8KVITEtxfvKL46NN6dhAZ5NGqwX6RWZpYqWQNewITeF0pHr8g==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/runtime-core": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7",
|
|
||||||
"csstype": "^2.6.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/shared": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-dn5FyfSc4ky424jH4FntiHno7Ss5yLkqKNmM/NXwANRnlkmqu74pnGetexDFVG5phMk9/FhwovUZCWGxsotVKg=="
|
|
||||||
},
|
|
||||||
"csstype": {
|
|
||||||
"version": "2.6.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz",
|
|
||||||
"integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q=="
|
|
||||||
},
|
|
||||||
"estree-walker": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
|
||||||
},
|
|
||||||
"lodash": {
|
|
||||||
"version": "4.17.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
|
||||||
},
|
|
||||||
"source-map": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
|
||||||
},
|
|
||||||
"to-fast-properties": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
|
||||||
},
|
|
||||||
"vue": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-8h4TikD+JabbMK9aRlBO4laG0AtNHRPHynxYgWZ9sq1YUPfzynd9Jeeb27XNyZytC7aCQRX9xe1+TQJuc181Tw==",
|
|
||||||
"requires": {
|
|
||||||
"@vue/compiler-dom": "3.0.7",
|
|
||||||
"@vue/runtime-dom": "3.0.7",
|
|
||||||
"@vue/shared": "3.0.7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@schlechtenburg/layout",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"description": "> TODO: description",
|
|
||||||
"author": "Benjamin Bädorf <hello@benjaminbaedorf.eu>",
|
|
||||||
"homepage": "",
|
|
||||||
"license": "GPL-3.0-or-later",
|
|
||||||
"main": "lib/index.ts",
|
|
||||||
"directories": {
|
|
||||||
"lib": "lib",
|
|
||||||
"test": "__tests__"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@git.b12f.io:b12f/schlechtenburg.git"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@schlechtenburg/core": "^0.0.0",
|
|
||||||
"vue": "^3.0.7"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
computed,
|
|
||||||
PropType,
|
|
||||||
} from 'vue';
|
|
||||||
import {
|
|
||||||
model,
|
|
||||||
} from '@schlechtenburg/core';
|
|
||||||
import {
|
|
||||||
getDefaultData,
|
|
||||||
ParagraphData,
|
|
||||||
} from './util';
|
|
||||||
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'sb-paragraph-display',
|
|
||||||
|
|
||||||
model,
|
|
||||||
|
|
||||||
props: {
|
|
||||||
data: {
|
|
||||||
type: Object as PropType<ParagraphData>,
|
|
||||||
default: getDefaultData,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const classes = computed(() => ({
|
|
||||||
'sb-paragraph': true,
|
|
||||||
[`sb-paragraph_align-${props.data.align}`]: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return () => <p
|
|
||||||
class={classes.value}
|
|
||||||
{...{
|
|
||||||
innerHTML: props.data.value,
|
|
||||||
}}
|
|
||||||
></p>;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -5,15 +5,8 @@ import {
|
||||||
ref,
|
ref,
|
||||||
Ref,
|
Ref,
|
||||||
onMounted,
|
onMounted,
|
||||||
watch,
|
|
||||||
PropType,
|
PropType,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
|
||||||
model,
|
|
||||||
useActivation,
|
|
||||||
SbToolbar,
|
|
||||||
SbSelect,
|
|
||||||
} from '@schlechtenburg/core';
|
|
||||||
import {
|
import {
|
||||||
getDefaultData,
|
getDefaultData,
|
||||||
ParagraphData,
|
ParagraphData,
|
||||||
|
@ -24,7 +17,10 @@ import './style.scss';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'sb-paragraph-edit',
|
name: 'sb-paragraph-edit',
|
||||||
|
|
||||||
model,
|
model: {
|
||||||
|
prop: 'block',
|
||||||
|
event: 'update',
|
||||||
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
blockId: { type: String, required: true },
|
blockId: { type: String, required: true },
|
||||||
|
@ -32,11 +28,6 @@ export default defineComponent({
|
||||||
type: (null as unknown) as PropType<ParagraphData>,
|
type: (null as unknown) as PropType<ParagraphData>,
|
||||||
default: getDefaultData,
|
default: getDefaultData,
|
||||||
},
|
},
|
||||||
onUpdate: { type: Function, default: () => {} },
|
|
||||||
onAppendBlock: { type: Function, default: () => {} },
|
|
||||||
onRemoveSelf: { type: Function, default: () => {} },
|
|
||||||
onActivateNext: { type: Function, default: () => {} },
|
|
||||||
onActivatePrevious: { type: Function, default: () => {} },
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
@ -51,121 +42,24 @@ export default defineComponent({
|
||||||
};
|
};
|
||||||
|
|
||||||
const inputEl: Ref<null|HTMLElement> = ref(null);
|
const inputEl: Ref<null|HTMLElement> = ref(null);
|
||||||
|
|
||||||
const { isActive, activate } = useActivation(props.blockId);
|
|
||||||
|
|
||||||
const focusInput = () => {
|
|
||||||
if (inputEl.value && isActive.value) {
|
|
||||||
inputEl.value.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
focusInput();
|
|
||||||
if (inputEl.value) {
|
if (inputEl.value) {
|
||||||
inputEl.value.innerHTML = localData.value;
|
inputEl.value.innerHTML = localData.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(isActive, focusInput);
|
|
||||||
|
|
||||||
watch(() => props.data, () => {
|
|
||||||
localData.value = props.data.value;
|
|
||||||
localData.align = props.data.align;
|
|
||||||
if (inputEl.value) {
|
|
||||||
inputEl.value.innerHTML = localData.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const onTextUpdate = ($event: Event) => {
|
|
||||||
localData.value = ($event.target as HTMLElement).innerHTML;
|
|
||||||
};
|
|
||||||
|
|
||||||
const classes = computed(() => ({
|
const classes = computed(() => ({
|
||||||
'sb-paragraph': true,
|
'sb-paragraph': true,
|
||||||
'sb-paragraph_focused': localData.focused,
|
'sb-paragraph_focused': localData.focused,
|
||||||
[`sb-paragraph_align-${localData.align}`]: true,
|
[`sb-paragraph_align-${localData.align}`]: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const setAlignment = ($event: Event) => {
|
|
||||||
props.onUpdate({
|
|
||||||
value: localData.value,
|
|
||||||
align: ($event.target as HTMLSelectElement).value,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFocus = () => {
|
|
||||||
localData.focused = true;
|
|
||||||
activate();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onBlur = () => {
|
|
||||||
localData.focused = false;
|
|
||||||
props.onUpdate({
|
|
||||||
value: localData.value,
|
|
||||||
align: localData.align,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onKeydown = ($event: KeyboardEvent) => {
|
|
||||||
if ($event.key === 'Enter' && !$event.shiftKey) {
|
|
||||||
const id = `${+(new Date())}`;
|
|
||||||
props.onAppendBlock({
|
|
||||||
id,
|
|
||||||
name: 'sb-paragraph',
|
|
||||||
data: getDefaultData(),
|
|
||||||
});
|
|
||||||
|
|
||||||
activate(id);
|
|
||||||
|
|
||||||
$event.preventDefault();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onKeyup = ($event: KeyboardEvent) => {
|
|
||||||
if ($event.key === 'Backspace' && localData.value === '') {
|
|
||||||
props.onRemoveSelf();
|
|
||||||
}
|
|
||||||
|
|
||||||
const selection = window.getSelection();
|
|
||||||
const node = selection?.focusNode;
|
|
||||||
const childNodes = Array.from(inputEl?.value?.childNodes || []);
|
|
||||||
const index = node ? childNodes.indexOf(node as ChildNode) : -1;
|
|
||||||
if (node === inputEl.value || index === 0 || index === childNodes.length -1) {
|
|
||||||
switch ($event.key) {
|
|
||||||
case 'ArrowDown':
|
|
||||||
props.onActivateNext();
|
|
||||||
break;
|
|
||||||
case 'ArrowUp':
|
|
||||||
props.onActivatePrevious();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div class={classes.value}>
|
<div class={classes.value}>
|
||||||
<SbToolbar>
|
|
||||||
<SbSelect
|
|
||||||
{...{
|
|
||||||
value: localData.align,
|
|
||||||
onChange: setAlignment,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option>left</option>
|
|
||||||
<option>center</option>
|
|
||||||
<option>right</option>
|
|
||||||
</SbSelect>
|
|
||||||
</SbToolbar>
|
|
||||||
<p
|
<p
|
||||||
class="sb-paragraph__input"
|
class="sb-paragraph__input"
|
||||||
ref={inputEl}
|
ref={inputEl}
|
||||||
contenteditable
|
contenteditable
|
||||||
onInput={onTextUpdate}
|
|
||||||
onFocus={onFocus}
|
|
||||||
onBlur={onBlur}
|
|
||||||
onKeydown={onKeydown}
|
|
||||||
onKeyup={onKeyup}
|
|
||||||
></p>
|
></p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue