cms: go
This commit is contained in:
parent
d8b729edc3
commit
a9e2ef03fe
|
@ -23,6 +23,11 @@ html {
|
|||
|
||||
--interact: #3f9cff;
|
||||
--interact-lite: #3f9cff;
|
||||
|
||||
--info: var(--interact);
|
||||
--success: green;
|
||||
--warning: orange;
|
||||
--error: red;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
|
@ -117,7 +117,14 @@ export const toHTMLString = (
|
|||
&& JSON.stringify(a) === JSON.stringify(f)));
|
||||
|
||||
console.log(c);
|
||||
for (
|
||||
for (let removedFormat of removedFormats) {
|
||||
const tool = tools.find(tool => tool.name === removedFormat.type);
|
||||
if (!tool) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tool.
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
|
|
2745
packages/core/package-lock.json
generated
2745
packages/core/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -9,8 +9,10 @@ body {
|
|||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
|
||||
--ex-nav-width: 60px;
|
||||
--ex-nav-expanded-width: 320px;
|
||||
--ex-nav-mobile-width: 60px;
|
||||
--ex-nav-desktop-width: 320px;
|
||||
|
||||
--ex-nav-width: var(--ex-nav-mobile-width);
|
||||
|
||||
--interact: #3f9cff;
|
||||
--interact-lite: #3f9cff;
|
||||
|
@ -39,6 +41,10 @@ body {
|
|||
--z-tree-block-select: 4000;
|
||||
--z-modal: 10000;
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
--ex-nav-width: var(--ex-nav-desktop-width);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
@ -49,20 +55,8 @@ body {
|
|||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&--page {
|
||||
flex-basis: 100%;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: var(--ex-nav-width);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
--ex-nav-width: var(--ex-nav-expanded-width);
|
||||
|
||||
&--edit-nav {
|
||||
position: unset;
|
||||
width: unset;
|
||||
flex-basis: var(--ex-nav-width);
|
||||
|
@ -70,4 +64,16 @@ body {
|
|||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&--page {
|
||||
flex-basis: 100%;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: var(--ex-nav-width);
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { NuxtPage } from '#components';
|
|||
import './app.scss';
|
||||
|
||||
const AdminNav = defineAsyncComponent(() => import('~~/components/_/Nav'));
|
||||
const Toaster = defineAsyncComponent(() => import('~~/components/_/Toaster'));
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
|
@ -12,6 +13,7 @@ export default defineComponent({
|
|||
<div class="ex-app">
|
||||
{me.value ? <AdminNav class="ex-app--edit-nav" /> : null}
|
||||
<NuxtPage class="ex-app--page" />
|
||||
{me.value ? <Toaster /> : null}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
12
packages/example-site/components/Page.scss
Normal file
12
packages/example-site/components/Page.scss
Normal file
|
@ -0,0 +1,12 @@
|
|||
.ex-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&--editor {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&_missing {
|
||||
margin: 4rem;
|
||||
}
|
||||
}
|
|
@ -1,42 +1,69 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import { SbMain, SbMode } from '@schlechtenburg/core';
|
||||
import {
|
||||
SbMain,
|
||||
SbButton,
|
||||
} from '@schlechtenburg/core';
|
||||
import PageToolbar from '~~/components/PageToolbar';
|
||||
import SbLayout from '@schlechtenburg/layout';
|
||||
import SbHeading from '@schlechtenburg/heading';
|
||||
import SbParagraph from '@schlechtenburg/paragraph';
|
||||
import SbImage from '@schlechtenburg/image';
|
||||
|
||||
import './Page.scss';
|
||||
|
||||
export default defineComponent({
|
||||
async setup() {
|
||||
const { me } = useMe();
|
||||
|
||||
const loggedIn = computed(() => !!me.value?.id);
|
||||
|
||||
const { currentPage } = useCurrentPage();
|
||||
const { setCurrentPageId, currentPage } = useCurrentPage();
|
||||
const block = computed(() => currentPage.value?.attributes?.block);
|
||||
|
||||
if (!block) {
|
||||
console.error('No block!');
|
||||
console.error('page', currentPage.value);
|
||||
}
|
||||
|
||||
const {
|
||||
mode,
|
||||
draft,
|
||||
updateDraft,
|
||||
} = useEditor();
|
||||
|
||||
const { edit } = useEditor();
|
||||
|
||||
watchEffect(() => {
|
||||
updateDraft(block.value!);
|
||||
});
|
||||
|
||||
const { insertPage } = usePages();
|
||||
|
||||
if (!block) {
|
||||
console.error('No block!');
|
||||
console.error('page', currentPage.value);
|
||||
}
|
||||
const createPageHere = () => {
|
||||
insertPage({
|
||||
id: 'draft',
|
||||
attributes: {
|
||||
title: 'New page',
|
||||
block: getNewPageBlock(),
|
||||
slug: '',
|
||||
parent: {
|
||||
data: {
|
||||
id: currentPage.value?.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
setCurrentPageId('draft');
|
||||
edit(currentPage.value?.attributes?.block!);
|
||||
};
|
||||
|
||||
return () => (
|
||||
<div class="ex-page">
|
||||
{loggedIn.value ? <PageToolbar></PageToolbar> : null}
|
||||
{draft.value
|
||||
? <SbMain
|
||||
class="ex-page"
|
||||
class="ex-page--editor"
|
||||
mode={mode.value}
|
||||
eventUpdate={(updatedBlock) => updateDraft(updatedBlock)}
|
||||
block={draft.value}
|
||||
|
@ -47,7 +74,14 @@ export default defineComponent({
|
|||
SbImage,
|
||||
]}
|
||||
/>
|
||||
: <div class="ex-page ex-page_corrupt">Corrupt page: {currentPage.value?.attributes?.slug} ({currentPage.value?.id})</div>}
|
||||
: <div class="ex-page ex-page_missing">
|
||||
<h1>Ooops!</h1>
|
||||
<p>This page does not exist yet. However, you can create it right now!</p>
|
||||
<SbButton
|
||||
type="button"
|
||||
onClick={() => createPageHere()}
|
||||
>Create a page here</SbButton>
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -43,13 +43,14 @@ export default defineComponent({
|
|||
return () => (
|
||||
<div class="ex-page-toolbar">
|
||||
<PageBreadcrumb />
|
||||
{currentPageId.value !== 'draft'
|
||||
{currentPageId.value !== 'draft' && !!currentPageId.value
|
||||
? <SbButton
|
||||
type="button"
|
||||
onClick={() => addChildPage()}
|
||||
>Add child page</SbButton>
|
||||
: null}
|
||||
{ mode.value === SbMode.View
|
||||
{!!currentPageId.value
|
||||
? (mode.value === SbMode.View
|
||||
? <SbButton
|
||||
type="button"
|
||||
onClick={() => edit(currentPage.value?.attributes?.block!)}
|
||||
|
@ -63,7 +64,8 @@ export default defineComponent({
|
|||
type="button"
|
||||
onClick={() => save()}
|
||||
>Save</SbButton>
|
||||
</>}
|
||||
</>)
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
max-width: 80vw;
|
||||
}
|
||||
|
||||
&--toggle {
|
||||
@media screen and (min-width: 1000px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -33,7 +39,7 @@
|
|||
&-action {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: var(--ex-nav-width);
|
||||
height: var(--ex-nav-mobile-width);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -58,6 +64,10 @@
|
|||
width: calc(100% - 60px);
|
||||
overflow: hidden;
|
||||
margin: 0px;
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,10 +81,4 @@
|
|||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
&--toggle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
packages/example-site/components/_/Toaster.scss
Normal file
28
packages/example-site/components/_/Toaster.scss
Normal file
|
@ -0,0 +1,28 @@
|
|||
.ex-toaster {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&--toast {
|
||||
margin: 1rem 2rem;
|
||||
border-radius: 4px;
|
||||
|
||||
&_info {
|
||||
background-color: var(--info);
|
||||
}
|
||||
|
||||
&_success {
|
||||
background-color: var(--success);
|
||||
}
|
||||
|
||||
&_warning {
|
||||
background-color: var(--warning);
|
||||
}
|
||||
|
||||
&_error {
|
||||
background-color: var(--error);
|
||||
}
|
||||
}
|
||||
}
|
15
packages/example-site/components/_/Toaster.tsx
Normal file
15
packages/example-site/components/_/Toaster.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { defineComponent } from 'vue';
|
||||
|
||||
import './Toaster.scss';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { toasts } = useToaster();
|
||||
|
||||
return () => (
|
||||
<nav class="ex-toaster">
|
||||
{toasts.value.map(toast => <div class={`ex-toaster--toast ex-toaster--toast_${toast.type}`}>{toast.content}</div>)}
|
||||
</nav>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -11,6 +11,7 @@ export const useEditor = () => {
|
|||
const {
|
||||
removePage,
|
||||
updatePage,
|
||||
insertPage,
|
||||
pages,
|
||||
} = usePages();
|
||||
|
||||
|
@ -25,6 +26,10 @@ export const useEditor = () => {
|
|||
draft.value = newDraft;
|
||||
}
|
||||
|
||||
const removeDraft = () => {
|
||||
draft.value = null;
|
||||
}
|
||||
|
||||
const edit = (block: IBlockData<any>) => {
|
||||
draft.value = block;
|
||||
mode.value = SbMode.Edit;
|
||||
|
@ -52,8 +57,8 @@ export const useEditor = () => {
|
|||
}
|
||||
|
||||
setMode(SbMode.View);
|
||||
updatePage(data.value?.createPage?.data?.attributes?.block);
|
||||
updateDraft(data.value?.createPage?.data?.attributes?.block);
|
||||
insertPage(data.value?.createPage?.data! as IPage);
|
||||
removeDraft();
|
||||
navigateTo(getPagePath(data.value?.createPage?.data! as IPage, pages.value));
|
||||
return;
|
||||
} else {
|
||||
|
|
48
packages/example-site/composables/toaster.ts
Normal file
48
packages/example-site/composables/toaster.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
export enum ToastType {
|
||||
SUCCESS = 'success',
|
||||
INFO = 'info',
|
||||
WARNING = 'warning',
|
||||
ERROR = 'error',
|
||||
}
|
||||
|
||||
export interface IToaster {
|
||||
type: ToastType,
|
||||
content: string;
|
||||
}
|
||||
|
||||
interface IToastedToaster extends IToaster {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_TOAST_TIME = 5000;
|
||||
|
||||
export const useToaster = () => {
|
||||
const toasts = useState<IToastedToaster[]>('toasts', () => []);
|
||||
|
||||
const removeToast = (id: number) => {
|
||||
toasts.value = toasts.value.filter(toast => toast.id !== id);
|
||||
};
|
||||
|
||||
const showToast = (toast: IToaster, time = DEFAULT_TOAST_TIME) => {
|
||||
const id = +(new Date());
|
||||
toasts.value = [
|
||||
...toasts.value,
|
||||
{
|
||||
...toast,
|
||||
id,
|
||||
},
|
||||
];
|
||||
|
||||
setTimeout(() => {
|
||||
removeToast(id);
|
||||
}, time);
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
return {
|
||||
toasts,
|
||||
showToast,
|
||||
removeToast,
|
||||
};
|
||||
};
|
|
@ -11,8 +11,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|||
const filters = pathParts.reduce((total, part) => {
|
||||
return {
|
||||
id: { ne: null },
|
||||
slug: { eq: part === '' ? null : part },
|
||||
|
||||
slug: { eq: part },
|
||||
parent: total,
|
||||
};
|
||||
}, {});
|
||||
|
|
12242
packages/example-site/package-lock.json
generated
12242
packages/example-site/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -22,6 +22,7 @@
|
|||
"@schlechtenburg/style": "^0.0.0",
|
||||
"event-target-polyfill": "^0.0.3",
|
||||
"nuxt-graphql-client": "^0.2.23",
|
||||
"nuxt-icon": "^0.1.8"
|
||||
"nuxt-icon": "^0.1.8",
|
||||
"sass": "^1.57.1"
|
||||
}
|
||||
}
|
||||
|
|
1953
packages/heading/package-lock.json
generated
1953
packages/heading/package-lock.json
generated
File diff suppressed because it is too large
Load diff
1953
packages/image/package-lock.json
generated
1953
packages/image/package-lock.json
generated
File diff suppressed because it is too large
Load diff
1737
packages/italic/package-lock.json
generated
1737
packages/italic/package-lock.json
generated
File diff suppressed because it is too large
Load diff
1953
packages/layout/package-lock.json
generated
1953
packages/layout/package-lock.json
generated
File diff suppressed because it is too large
Load diff
1953
packages/paragraph/package-lock.json
generated
1953
packages/paragraph/package-lock.json
generated
File diff suppressed because it is too large
Load diff
1953
packages/standalone/package-lock.json
generated
1953
packages/standalone/package-lock.json
generated
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue