@distromade/distro-cms

1,008
cms
Install
npm install @distromade/distro-cms
Latest Version:0.6.1
Vue:3
License:MIT
Last Updated:Jan 13, 2024
Links: GitLab  ·  npm
Maintainer: jareko999

Distro CMS

Distro CMS is an open source CMS used to edit Nuxt 3 Content pages, right from your site. Content updates in real-time with 2-way binding.

Usage

1. Create a Nuxt 3 Content project

2. Install Distro CMS and Distro UI

# npm
npm install @distromade/distro-cms @distromade/distro-ui

# yarn
yarn add @distromade/distro-cms @distromade/distro-ui

# pnpm
pnpm add @distromade/distro-cms @distromade/distro-ui

3. Update app.vue

<script setup lang="ts">
import '@distromade/distro-ui/style.css';
import '@distromade/distro-cms/style.css';
import { LoginForm, PageWrapper } from '@distromade/distro-cms';
import type {
  LoginFunctionReturnType,
  UnsavedChangesType,
} from '@distromade/distro-cms/dist/types';

const contentStore = useContentStore();
const route = useRoute();

function login() {
  // Auth logic
}

function save(changes: UnsavedChangesType) {
   // Save changes to your project
   console.log('Saving changes:', JSON.parse(JSON.stringify(changes)));
}
</script>

<template>
  <PageWrapper :content="contentStore.content" :route="route.path" :save="save">
    <template #login-form>
      <LoginForm :login="login" />
    </template>

    <NuxtLoadingIndicator color="#222" :height="2" :throttle="400" />
    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
  </PageWrapper>
</template>

4. How to open the CMS in your project

Add a button or link to open the CMS

<script lang="ts" setup>
   import { useGlobalCMSState } from '@distromade/distro-cms';

   const { showEditor } = useGlobalCMSState();

   function toggleEditor() {
      showEditor.value = !showEditor.value;
   }
</script>
<template>
   <button @click="toggleEditor" />
</template>

5. Add useThemeContent composable

Under /composables, create useThemeContent.ts:

import { computed } from 'vue';
import { v4 as uuid } from 'uuid';
import { useGlobalCMSState } from '@distromade/distro-cms';
import type {
  MarkdownElement,
  PlainTextElement,
  PageContentType,
} from '@distromade/distro-cms/dist/types';

export async function useThemeContent<
  M = Record<string, MarkdownElement>,
  P = Record<string, PlainTextElement>,
>(contentPath: string) {
  const { pageContent } = useGlobalCMSState();
  const contentStore = useContentStore();

  const content = (await queryContent(contentPath).findOne()) as unknown as PageContentType;
  contentStore.setContent(content);

  const mdElements = computed(() => {
    const { markdownElements } = content.cms;
    const { markdownElements: cmsMdElements } = JSON.parse(
      JSON.stringify(pageContent.value?.cms ?? {}),
    );
    if (cmsMdElements) return cmsMdElements as M;
    return markdownElements as M;
  });

  const textElements = computed(() => {
    const { plainTextElements } = content.cms;
    const { plainTextElements: cmsTextElements } = JSON.parse(
      JSON.stringify(pageContent.value?.cms ?? {}),
    );
    if (cmsTextElements) return cmsTextElements as P;
    return plainTextElements as P;
  });

  const pageTitle = computed<string>(() => {
    if (pageContent.value) return pageContent.value.cms.pageTitle;
    return content.cms.pageTitle;
  });

  /**
   * This is a hack to force elements bound to mdElements via v-html to re-render
   * when the pageContent changes.
   */
  const key = computed(() => {
    // does nothing; just triggers reactivity:
    pageContent.value?.cms;
    return uuid();
  });

  return {
    key,
    mdElements,
    textElements,
    pageTitle,
  };
}

6. Add useContentStore in stores

In your /stores directory, create useContentStore.ts:

import { defineStore } from 'pinia';
import type { PageContentType } from '@distromade/distro-cms/dist/types';

export interface ContentState {
  content: PageContentType | null;
}

export const useContentStore = defineStore('content', {
  state(): ContentState {
    return {
      content: null as PageContentType | null,
    };
  },
  actions: {
    setContent(content: PageContentType | null) {
      this.content = content;
    },
  },
});

7. Create .vue page

In your /pages directory, create a .vue page. In this example, we'll use about.vue for an about page.

<script lang="ts" setup>
   import { md, text } from '@distromade/distro-cms';
   import json from '~/content/about.json';
   import { useThemeContent } from '@/composables/useThemeContent';

   type Content = typeof json;
   type MD = Content['cms']['markdownElements'];
   type Text = Content['cms']['textElements'];

   const { mdElements, textElements, pageTitle, key } = await useThemeContent<MD, Text>('/about');

   useHead({
      title: () => pageTitle.value,
   });
</script>

<template>
  <section>
   <h1>{{ text(textElements.title) }}</h1>
   <div v-html="md(mdElements.about)" />
  </section>
</template>

You can render either as text in {{ }} or in v-html.

8. Create the .json file

In your /content directory, create about.json.

{
  "contentId": "about",
  "cms": {
    "pageTitle": "About us",
    "pageDescription": "About our company...",
    "plainTextElements": {
      "title": {
        "label": "About title",
        "text": "About Killer Coffee Co."
      }
    },
    "markdownElements": {
      "about": {
        "label": "About description",
        "markdown": "This is our company bio, **written in markdown**."
      }
    }
  }
}

9. Run project locally

npm run dev

Now, you can edit your static content with the CMS, right from your project.

Distro UI


Testing the CMS with Storyboook

To run the Storybook preview locally, while developing, run the following command:

Using NPM
npm install
npm run storybook
Using Yarn
yarn install
yarn storybook
Using PNPM
pnpm install
pnpm storybook

Related Packages

onebuild

Used to bundle and serve manifests web component that build on any UI frameworks...

10,419 0
onesystem-shareds

Provide full stack client side to build on top.

8,796
q42-cms-components

Front-end package that provides a UI on top of the QMS back-end

6,213
onebuild-plugin-rollup

Used to bundle manifests.

4,725 0
onebuild-plugin-hmr-vue

Used to bundle manifests.

4,250 0

Version History

Version Released Vue License
0.6.1 ^3.3.7 MIT
0.6.0 ^3.3.7 MIT
0.5.0 ^3.3.7 MIT
0.4.9 ^3.3.7 MIT
0.4.8 ^3.3.7 MIT